我正在使用视图模型属性 SelectedUser 绑定TextBox文本,但在更新时,与此属性关联的控件不会反映更改。
我的ViewModel&模型
public class UserViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged.members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private ObservableCollection<UserModel> _userList;
public ObservableCollection<UserModel> UserList
{
get
{
return _userList;
}
set
{
_userList = value;
}
}
private UserModel _selectedUser;
public UserModel SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = value;
OnPropertyChanged("SelectedUser");
}
}
public UserViewModel()
{
UserManager manager = new UserManager();
var FilterCombo = new List<ComboItem> {
new ComboItem{Text = "Name", Value = "Name"},
new ComboItem{Text = "Owner", Value = "Owner"},
new ComboItem{Text = "Email", Value = "Email"},
new ComboItem{Text = "Contact Number", Value = "Contact"},
new ComboItem{Text = "Address", Value = "Address"},
};
var filter = new FilterModel
{
FilterItems = FilterCombo,
FilterSelected = FilterCombo.Where(t => t.Text == "Name").FirstOrDefault(),
FilterValue = ""
};
_userList = new ObservableCollection<UserModel>(manager.GetList(filter));
}
}
public class UserModel :INotifyPropertyChanged
{
public Database.User user { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
SelectedUser 属性正在更新DataGrid选择,如下所示
<DataGrid Grid.Row="1" ItemsSource="{Binding Path=UserList}" AutoGenerateColumns="False"
HorizontalAlignment="Stretch" IsReadOnly="True" SelectionMode="Single"
SelectedValue="{Binding SelectedUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
查看内容
<Label Content="Login ID" Grid.Row="1"/>
<TextBox Grid.Row="2" Text="{Binding SelectedUser.Login, Mode=TwoWay}" x:Name="txtLogin" />
<Label Content="Password" Grid.Row="3"/>
<TextBox Grid.Row="4" Text="{Binding SelectedUser.Password, Mode=TwoWay}" x:Name="txtPassword"/>
<Label Content="Full Name" Grid.Row="5"/>
<TextBox Grid.Row="6" Text="{Binding SelectedUser.Name, Mode=TwoWay}" x:Name="txtName"/>
<Label Content="Email" Grid.Row="7"/>
<TextBox Grid.Row="8" Text="{Binding SelectedUser.Email, Mode=TwoWay}" x:Name="txtEmail"/>
<Label Content="Contact" Grid.Row="9"/>
<TextBox Grid.Row="10" Text="{Binding SelectedUser.Contact, Mode=TwoWay}" x:Name="txtContact" />
答案 0 :(得分:1)
您的代码有多个问题。您的UserModel
类没有绑定xaml的属性(行名称,电子邮件,联系人,...)。并且您不会为要绑定的所有属性触发OnPropertyChanged
INotifyPropertyChange
方法。我建议进行以下更改。
ViewModel基类
我会使用BindableBase
类PRISM作为视图模型的基类,它们具有更改属性,因此您不需要在每个视图模型中实现INotifyPropertyChanged
接口
/// <summary>
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
/// </summary>
public abstract class BindableBase : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <typeparam name="T">The type of the property that has a new value</typeparam>
/// <param name="propertyExpression">A Lambda expression representing the property that has a new value.</param>
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
this.OnPropertyChanged(propertyName);
}
}
<强>模型强>
另外,我并不建议使用绑定的属性,因为你的模型类会因为需要实现INotifyPropertyChanged
而变得丑陋。因此,如果您的Database.User
课程不包含任何逻辑或不需要的字段,请将其用作模型或创建您自己的字段:
public class User
{
public string Name { get; set; }
public string Password { get; set; }
public string Login { get; set; }
public string Email { get; set; }
public string Contact { get; set; }
public override string ToString()
{
return Name;
}
}
<强>扩展强>
要将Database.User
转换为模型,您可以创建扩展方法
public static class DatabaseUserExtensions
{
public static User ToModel(this Database.User x)
{
//convert the object of type Database.User to your User model clas here
}
public static IEnumerable<User> ToModels(this IEnumerable<Database.User> xs)
{
return xs.Select(x => x.ToModel());
}
}
<强>视图模型强>
因为您的模型没有实现INotifyPropertyChanged
,所以您需要为您的属性创建包装器。因此,将viewModel更改为以下内容:
//use the BindableBase class as base class
public class UserViewModel : BindableBase
{
//use the correct Model class (the newly created or Database.User if it
//is sufficient).
//also IEnumerable is sufficient
//there is no need for an private field
public IEnumerable<User> UserList { get; private set; }
//wrapper for SelectedUser.Name
public string SelectedUserName
{
get { return SelectedUser.Name; }
set
{
SelectedUser.Name = value;
OnPropertyChanged(nameof(SelectedUserName));
}
}
//add wrappers for the other properties like
//SelectedUserPassword, SelectedUserContact, ...
//use the correct Model class
//(the newly created or Database.User if it is sufficient)
private User _selectedUser;
public User SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = value;
// call the OnPropertyChanged of BindableBase
// (I would also recommend using nameof)
OnPropertyChanged(nameof(SelectedUser));
// call OnPropertyChanged for the other wrapper properties like
// SelectedUserPassword, SelectedUserContact, ...
OnPropertyChanged(nameof(SelectedUserName));
// ...
}
}
public UserViewModel()
{
UserManager manager = new UserManager();
var FilterCombo = new List<ComboItem> {
new ComboItem{Text = "Name", Value = "Name"},
new ComboItem{Text = "Owner", Value = "Owner"},
new ComboItem{Text = "Email", Value = "Email"},
new ComboItem{Text = "Contact Number", Value = "Contact"},
new ComboItem{Text = "Address", Value = "Address"},
};
var filter = new FilterModel
{
FilterItems = FilterCombo,
FilterSelected = FilterCombo.Where(t => t.Text == "Name").FirstOrDefault(),
FilterValue = ""
};
//this uses now the extension method
//(your manager should be doing the converting and return the model
//instead of the database user if the database user class is
//not sufficient)
_userList = manager.GetList(filter).ToModels();
}
}
查看强>
您在xaml中设置的所有属性都不是必需的(默认情况下应该为true)。
<DataGrid Grid.Row="1" ItemsSource="{Binding Path=UserList}" AutoGenerateColumns="False"
HorizontalAlignment="Stretch" IsReadOnly="True" SelectionMode="Single"
SelectedValue="{Binding Path=SelectedUser}">
并根据viewmodel中的更改
更改xaml的其余部分<Label Content="Login ID" Grid.Row="1"/>
<TextBox Grid.Row="2" Text="{Binding Path=SelectedUserLogin}" x:Name="txtLogin" />
<Label Content="Password" Grid.Row="3"/>
<TextBox Grid.Row="4" Text="{Binding Path=SelectedUserPassword}" x:Name="txtPassword"/>
<Label Content="Full Name" Grid.Row="5"/>
<TextBox Grid.Row="6" Text="{Binding Path=SelectedUserName}" x:Name="txtName"/>
<Label Content="Email" Grid.Row="7"/>
<TextBox Grid.Row="8" Text="{Binding Path=SelectedUserEmail}" x:Name="txtEmail"/>
<Label Content="Contact" Grid.Row="9"/>
<TextBox Grid.Row="10" Text="{Binding Path=SelectedUserContact}" x:Name="txtContact" />
答案 1 :(得分:0)
首先,SelectedUser
类型为UserModel
的{{1}}并不具备所有这些属性(登录名,密码,名称...),我想你的Database.User
拥有它们。
我的简单解决方案是,您将所有View内容DataContext绑定到SelectedUser
,并将textboxe的绑定更改为 - user.Login,user.Password ...