I am trying to make my very first Silverlight App ever, but I can’t get the LogOn function to work, can you help me? This should properly be super simple for all of you, I will show you my two files: LogOn.xaml.cs and LogOnViewModel.cs
Apparently the problem is that UserId gets not set early enough to be availble in LogOn.xaml.cx when I need it, can you help me make it work, that would lift my moment quite a bit 🙂
public partial class LogOn : PhoneApplicationPage
{
public LogOn()
{
InitializeComponent();
this.DataContext = LogOnViewModel.Instance;
}
private void btnLogOn_Click(object sender, RoutedEventArgs e)
{
if ((!string.IsNullOrEmpty(txtEmailAddress.Text)) && (!string.IsNullOrEmpty(txtPassword.Password)))
{
txbLogonMessage.Text = "";
LogOnViewModel.Instance.UserLogin(txtEmailAddress.Text, txtPassword.Password);
if (LogOnViewModel.Instance.UserId > 0)
NavigationService.Navigate(new Uri("/_2HandApp;component/Views/Main.xaml", UriKind.Relative));
else
txbLogonMessage.Text = "Login was unsuccessful. The user name or password provided is incorrect. Please correct the errors and try again. ";
}
}
}
public sealed class LogOnViewModel : INotifyPropertyChanged
{
public static LogOnViewModel Instance = new LogOnViewModel();
//public static int userId;
private SHAServiceClient WS;
private int userId;
public int UserId
{
get
{
return userId;
}
set
{
userId = value;
this.RaisePropertyChanged("UserId");
}
}
private LogOnViewModel()
{
WS = new SHAServiceClient();
WS.UserLoginCompleted += new EventHandler<UserLoginCompletedEventArgs>(WS_UserLoginCompleted);
}
void WS_UserLoginCompleted(object sender, UserLoginCompletedEventArgs e)
{
if (e.Error == null)
{
this.UserId = e.Result;
}
}
public void UserLogin(string email, string password)
{
WS.UserLoginAsync(email, password);
}
/* Implementing the INotifyPropertyChanged interface. */
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null))
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The cause of the problem is what has been highlighted by @flq. You’re making an asynchronous call, meaning that you won’t get the expected result right away (in your case, the UserId being assigned), but instead, you can subscibe to the
Completedevent (or provide a callback) to handle things when the asynchronous task finishes.Now, the “MVVM way” to do this (or at least what I would do) is as follows: first of all, go get MVVM Light! it’s a lightweight MVVM framework which would be very helpful. You should have your ViewModel class implement the
ViewModelBasebase class from MVVMLight, this would provide the change notification and messaging as well as other useful stuff. Then, you should encapsulate the login functionality in a command to be able to wire up it up from xaml, for that you can use MVVMLight’sRelayCommand. Once the login is complete, you can just send a message to your view letting it know that (in a pretty decoupled way), and the view can simply initiate the navigation.Here’s the bits of code for that:
for the xaml:
in the code behind:
You can see that there is a little bit more (but straightforward) code in the ViewModel and less in the code behind. This also took advantage of DataBinding which is in the heart of MVVM.
Hope this helps 🙂
P.S: the LoginCompletedMessage class is just an empty class in this case (used just to define the type message), but you can use it to send more info (maybe you still want to have the UserId sent)