【问题标题】:Model is null in View Model模型在视图模型中为空
【发布时间】:2016-03-07 21:54:08
【问题描述】:

我有一个由属性组成的模型,这个模型在我的视图模型中被引用。

我正在以编程方式更改模型属性的值(从数据库中获取数据)。 当这些值发生变化时,“OnPropertyChanged”事件会在模型中触发,正如预期的那样。

但是,我的观点并没有随着这些变化而更新。

调试我的视图模型显示该模型为空。

对 MVVM 非常陌生,并且已经学习了一段时间的教程,但似乎无法弄清楚这一点。

我的代码如下(无关部分省略)

型号

 class User : INotifyPropertyChanged
{
    private int _ID;
    private string _FirstName;
    private string _SurName;
    private string _Email;
    private string _ContactNo;

    public string FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            _FirstName = value;
            OnPropertyChanged("FirstName");
        }
    }


    #region INotifyPropertyChanged Members
    private event PropertyChangedEventHandler PropertyChangedEvent;

    public event PropertyChangedEventHandler PropertyChanged
    {
        add { PropertyChangedEvent += value; }
        remove { PropertyChangedEvent -= value; }
    }


    protected void OnPropertyChanged(string prop)
    {
        if (PropertyChangedEvent != null)
            PropertyChangedEvent(this, new PropertyChangedEventArgs(prop));
    }

    #endregion
}

查看模型(不要认为我需要在此处更改属性,但无论如何都要将其放入以防万一)

class MainWindowVM : INotifyPropertyChanged
{
    public User UserModel { get; set; }

    public MainWindowVM()
    {
        var test = UserModel.FirstName;

    }


    #region INotifyPropertyChanged Members
    private event PropertyChangedEventHandler PropertyChangedEvent;

    public event PropertyChangedEventHandler PropertyChanged
    {
        add { PropertyChangedEvent += value; }
        remove { PropertyChangedEvent -= value; }
    }


    protected void OnPropertyChanged(string prop)
    {
        if (PropertyChangedEvent != null)
            PropertyChangedEvent(this, new PropertyChangedEventArgs(prop));
    }

    #endregion

}

查看

    <Window x:Class="ProjectName.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:ProjectName.ViewModels">


    <Window.DataContext>
        <vm:MainWindowVM/>
    </Window.DataContext>


   <Grid Style="{DynamicResource ResourceKey=MainGrid}">
        <Label Content="Logged in as:" HorizontalAlignment="Right" VerticalAlignment="Top" Padding="0,0,200,0"/>
        <Label Content="{Binding Path=UserModel.FirstName}" HorizontalAlignment="Right" VerticalAlignment="Top" Padding="0,0,150,0"/>
    </Grid> 
</Window>

此处设置的用户模型

 public bool Login(string email, string password)
    {

        var userOb = new Models.User(); // new instance of user object

        using (SqlConnection con = new SqlConnection(FactoryManager.Properties.Resources.ConnectionString))
        {
            using (SqlCommand cmd = new SqlCommand("usp_login", con))
            {
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@email", SqlDbType.VarChar).Value = email;

                con.Open();
                cmd.ExecuteNonQuery();

                SqlDataReader dr = cmd.ExecuteReader();

                if (dr.HasRows)
                {

                    string dbHash = null;
                    while (dr.Read())
                    {
                        dbHash = dr[1].ToString(); // get db hash value from reader
                    }

                    // if password hash matched DB then log in
                    if (Encryption.ValidatePassword(password, dbHash))
                    {
                        con.Close();
                        GetUser(userOb, email); // gets the current user
                        return true; // logged in
                    }
                    else
                    {
                        return false;
                    }

                }
                else
                {
                    return false; //invalid login
                }

            }
        }

    }

    // gets the logged in user and populates the user object, returns this
    public Models.User GetUser(Models.User userOb, string email)
    {
        using (SqlConnection con = new SqlConnection(FactoryManager.Properties.Resources.ConnectionString))
        {
            using (SqlCommand cmd = new SqlCommand("usp_getUser", con))
            {
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@email", SqlDbType.VarChar).Value = email;

                con.Open();
                cmd.ExecuteNonQuery();

                SqlDataReader dr = cmd.ExecuteReader();

                if (dr.HasRows)
                {

                    while (dr.Read())
                    {

                        userOb.ID = Int32.Parse(dr[0].ToString());
                        userOb.FirstName = dr[1].ToString();
                        userOb.SurName = dr[2].ToString();
                        userOb.Email = dr[3].ToString();
                        userOb.ContactNo = dr[4].ToString();
                        //Password = dr[4].ToString();
                        //loginAttempts = dr[5].ToString(); 
                    }

                    // GET ALLOCATED LINES AND POPULATE THE OBJECT

                }
                else
                {
                    return null; //invalid login
                }

            }
        }

        return userOb;
    }

【问题讨论】:

    标签: c# wpf xaml mvvm .net-4.5


    【解决方案1】:

    当然是null

    您的MainWindowVM 中有UserModel 属性,但您没有在任何地方初始化它(至少不是在您粘贴的代码中)。

    在视图模型代码中的某处应该有一个UserModel = new User(/* arguments */);

    【讨论】:

    • 但是然后在VM中初始化一个新模型会重置所有属性,属性会在视图显示之前更新
    • 那你在哪里设置UserModel 属性呢?你能把它贴在你的帖子里吗?通常这些是通过构造函数注入、属性注入、简单地从视图模型更新的外部(这里不是这种情况)、视图模型本身(例如服务调用)来设置的。现在就想到这些。我在这里没有看到任何这些。
    • 更新了我的问题.. 抱歉任何不相关的代码
    • 何时何地调用这些方法? LoginGetUser。我想说您必须以​​某种方式将 GetUser 方法返回的 User 实例分配给您的视图模型的 UserModel 属性。如果这些位于内部,可以说一个名为 LoginService 的服务,您通常会将其注入到视图模型的构造函数中并在其上调用一些返回 User 的方法:UserModel = _loginService.GetUser();
    【解决方案2】:

    UserModel 本身不支持 INPC,因此更改它不会更新视图。您还将 INPC 添加到您的模型中,它通常不属于该模型。

    说实话,我认为您需要阅读一些有关 MVVM 工作原理的书籍,但我会尝试给出一些初步的建议。你的 MainViewModel 需要这样的东西:

        private UserViewModel _User;
        public UserViewModel User
        {
            get { return this._User; }
            set { this._User = value; RaisePropertyChanged(); }
        }
    

    您的用户类通常来自您的数据层,因此它通常不会执行 INPC,尤其是在您进行代码优先开发时:

    public class User
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string SurName { get; set; }
        public string Email { get; set; }
        public string ContactNo { get; set; }
    }
    

    (*这有例外,但现在让我们保持简单)。

    你的视图模型是两者之间的粘合剂,形成了视图和视图模型之间的绑定,所以是这样的:

    public class UserViewModel
    {
        private User Model;
    
        public UserViewModel(User model)
        {
            this.Model = model;
        }
    
        public int ID
        {
            get { return this.Model.ID; }
            set { this.Model.ID = value; RaisePropertyChanged(); }
        }
    
        public string FirstName
        {
            get { return this.Model.FirstName; }
            set { this.Model.FirstName = value; RaisePropertyChanged(); }
        }
    
        // etc
    }
    

    是的,这意味着需要在 UI 中进行编辑并且还需要响应底层视图模型中的更改的任何字段都必须在视图和视图模型层中复制。有一些方法可以将其自动添加到模型层,但它通常需要对模型和/或数据库层进行特定于实现的修改,因此我不会在这里介绍。

    从这一切中得到的一点是,视图模型正是:视图的逻辑表示,它提供 INPC 供视图使用。如果您希望代码修改模型,并且希望该更改反映在视图中,那么您必须通过视图模型执行此操作,以便更改传播。

    【讨论】:

    • 你可以定义 INPC 吗?
    • @RichardJune INotifyPropertyChanged,如果您使用像 MVVM Lite 这样的库,它将在基本视图模型类中为您完成,否则您必须自己添加它。无论哪种方式,您都必须在属性设置器本身中引发更改的事件。
    猜你喜欢
    • 1970-01-01
    • 2014-10-04
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    相关资源
    最近更新 更多