模型在视图模型中为空
Model is null in View Model
我有一个由属性组成的模型,这个模型在我的视图模型中被引用。
我正在以编程方式更改模型属性的值(从数据库获取数据)。
当这些值发生变化时,"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;
}
当然是null
。
您的 MainWindowVM
中有 UserModel
属性,但您没有在任何地方初始化它(至少没有在您粘贴的代码中)。
视图模型代码中应该有一个UserModel = new User(/* arguments */);
。
UserModel 本身不支持 INPC,因此更改它不会更新视图。您还将 INPC 添加到您的模型中,它通常不属于该模型。
老实说,我认为您需要阅读几本有关 MVVM 工作原理的书籍,但我会尝试提供一些初步指导。您的 MainViewModel 需要这样的东西:
private UserViewModel _User;
public UserViewModel User
{
get { return this._User; }
set { this._User = value; RaisePropertyChanged(); }
}
您的用户 class 通常来自您的数据层,因此它通常不会执行 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 中编辑并且还需要响应基础视图模型中的更改的任何字段都必须在视图和视图模型层中复制。有一些方法可以将它自动添加到模型层,但它通常需要对模型 and/or 数据库层进行特定于实现的修改,所以我不会在这里介绍它。
从这一切中得出的观点是,视图模型正是:视图的逻辑表示,提供 INPC 供视图使用。如果您希望代码修改模型,并且希望该更改反映在视图中,那么您必须通过视图模型来执行此操作,以便更改可以传播。
我有一个由属性组成的模型,这个模型在我的视图模型中被引用。
我正在以编程方式更改模型属性的值(从数据库获取数据)。 当这些值发生变化时,"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;
}
当然是null
。
您的 MainWindowVM
中有 UserModel
属性,但您没有在任何地方初始化它(至少没有在您粘贴的代码中)。
视图模型代码中应该有一个UserModel = new User(/* arguments */);
。
UserModel 本身不支持 INPC,因此更改它不会更新视图。您还将 INPC 添加到您的模型中,它通常不属于该模型。
老实说,我认为您需要阅读几本有关 MVVM 工作原理的书籍,但我会尝试提供一些初步指导。您的 MainViewModel 需要这样的东西:
private UserViewModel _User;
public UserViewModel User
{
get { return this._User; }
set { this._User = value; RaisePropertyChanged(); }
}
您的用户 class 通常来自您的数据层,因此它通常不会执行 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 中编辑并且还需要响应基础视图模型中的更改的任何字段都必须在视图和视图模型层中复制。有一些方法可以将它自动添加到模型层,但它通常需要对模型 and/or 数据库层进行特定于实现的修改,所以我不会在这里介绍它。
从这一切中得出的观点是,视图模型正是:视图的逻辑表示,提供 INPC 供视图使用。如果您希望代码修改模型,并且希望该更改反映在视图中,那么您必须通过视图模型来执行此操作,以便更改可以传播。