简体   繁体   English

非常非常简单的MVVM问题

[英]Very very simple MVVM problem

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? 我试图制作我的第一个Silverlight应用程序,但我无法使LogOn功能起作用,你能帮助我吗? This should properly be super simple for all of you, I will show you my two files: LogOn.xaml.cs and LogOnViewModel.cs 对于你们所有人来说这应该是非常简单的,我将向你展示我的两个文件:LogOn.xaml.cs和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 :-) 显然问题是UserId没有及早设置,以便在我需要时可以在LogOn.xaml.cx中使用它,你能帮助我使它工作,这会提升我的时刻:-)

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. 问题的原因是@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 Completed event (or provide a callback) to handle things when the asynchronous task finishes. 您正在进行异步调用,这意味着您将无法立即获得预期结果(在您的情况下,正在分配UserId),而是您可以将subscibe subcibe到Completed事件(或提供回调)来处理事物异步任务完成时。

Now, the "MVVM way" to do this (or at least what I would do) is as follows: first of all, go get MVVM Light! 现在,执行此操作的“MVVM方式”(或者至少我会做的)如下:首先,去获取MVVM Light! it's a lightweight MVVM framework which would be very helpful. 它是一个轻量级的MVVM框架,非常有用。 You should have your ViewModel class implement the ViewModelBase base class from MVVMLight, this would provide the change notification and messaging as well as other useful stuff. 您应该让您的ViewModelBase类从MVVMLight实现ViewModelBase基类,这将提供更改通知和消息以及其他有用的东西。 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's RelayCommand . 然后,您应该将登录功能封装在一个命令中,以便能够从xaml连接它,因为您可以使用MVVMLight的RelayCommand 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: 这是代码的一部分:

public class LogOnViewModel : ViewModelBase
{
    private SHAServiceClient WS;
    public LogOnViewModel()
    {
       WS = new SHAServiceClient();
       WS.UserLoginCompleted += new EventHandler<UserLoginCompletedEventArgs>(WS_UserLoginCompleted);
       LoginCommand = new RelayCommand(UserLogin);
    }
    private int userId;
    public int UserId
    {
       get { return userId; }
       set
       {
          userId = value;
          RaisePropertyChanged(()=>UserId);
       }
    }
    private int password;
    public int Password
    {
       get { return password; }
       set
       {
          password = value;
          RaisePropertyChanged(()=>Password);
       }
    }
    private int username;
    public int Username
    {
       get { return username; }
       set
       {
          username = value;
          RaisePropertyChanged(()=>Username);
       }
    }
    private int loginErrorMessage;
    public int LoginErrorMessage
    {
       get { return loginErrorMessage; }
       set
       {
          loginErrorMessage = value;
          RaisePropertyChanged(()=>LoginErrorMessage);
       }
    }
    void WS_UserLoginCompleted(object sender, UserLoginCompletedEventArgs e)
    {
       if (e.Error == null)
       {
          this.UserId = e.Result;
          // send a message to indicate that the login operation has completed
          Messenger.Default.Send(new LoginCompleteMessage());
       }
    }
    public RelayCommand LoginCommand {get; private set;}
    void UserLogin()
    {
       WS.UserLoginAsync(email, password);
    }
}

for the xaml: 对于xaml:

<TextBox Text="{Binding Username, Mode=TwoWay}"/>
<TextBox Text="{Binding Password, Mode=TwoWay}"/>
<Button Command="{Binding LoginCommand}"/>
<TextBlock Text="{Binding LoginErrorMessage}"/>    

in the code behind: 在后面的代码中:

public partial class LogOn : PhoneApplicationPage
{
    public LogOn()
    {
        InitializeComponent();
        this.DataContext = new LogOnViewModel();
        Messenger.Default.Register<LoginCompletedMessage>(
                            this,
                            msg=> NavigationService.Navigate(
                                    new Uri("/_2HandApp;component/Views/Main.xaml",
                                    UriKind.Relative) );
    }
  ....
}

You can see that there is a little bit more (but straightforward) code in the ViewModel and less in the code behind. 您可以看到ViewModel中有更多(但简单明了)的代码,而后面的代码则更少。 This also took advantage of DataBinding which is in the heart of MVVM. 这也利用了DataBinding,它位于MVVM的核心。

Hope this helps :) 希望这可以帮助 :)

PS: 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) PS:在这种情况下,LoginCompletedMessage类只是一个空类(仅用于定义类型消息),但您可以使用它来发送更多信息(可能您仍希望发送UserId)

Well, you're calling an async version of a login WS.UserLoginAsync , which means the execution moves on and indeed there is no user id when you check for it. 好吧,你正在调用一个登录WS.UserLoginAsync的异步版本,这意味着执行继续,当你检查它时确实没有用户ID。

You aren't really doing MVVVM here, but anyway, let's go with the flow. 你在这里并没有真正做MVVVM,但无论如何,让我们顺其自然。 Have an event on your "Viewmodel" that is raised when the login process is finished ( WS_UserLoginCompleted ). 在登录过程完成时引发的“Viewmodel”上有一个事件( WS_UserLoginCompleted )。 You can handle it and trigger Navigation in an event-handler of that event. 您可以处理它并在该事件的事件处理程序中触发导航。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM