我很新于WPF,所以我想从简单的开始:一个窗口,允许用户管理用户。该窗口包含一个
然而,发生的情况是,一旦用户在其中一个输入控件中进行更改,
以下是视图的XAML代码:
视图模型(
显然,我希望使用纯MVVM模式来实现这个。我已经尝试将
我还查看了这篇 SO问题,该问题建议在ViewModel上使用“selected”属性。我的原始实现,如上所述,已经有了这样一个属性(称为“CurrentUser”),但是使用当前的绑定配置,网格仍会在用户进行更改时更新。
如果您能提供任何帮助,将不胜感激,因为我已经在解决这个问题上碰壁了几个小时。如果我漏掉了任何内容,请评论并我会更新帖子。谢谢。
DataGrid
和几个输入控件,用于添加或编辑用户。当用户在网格中选择记录时,数据绑定到输入控件。然后用户可以进行必要的更改并单击“保存”按钮以保留更改。然而,发生的情况是,一旦用户在其中一个输入控件中进行更改,
DataGrid
中对应的数据也会在“保存”按钮被点击之前更新。我希望DataGrid
仅在用户单击“保存”后才更新。以下是视图的XAML代码:
<Window x:Class="LearnWPF.Views.AdminUser"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vms="clr-namespace:LearnWPF.ViewModels"
Title="User Administration" Height="400" Width="450"
ResizeMode="NoResize">
<Window.DataContext>
<vms:UserViewModel />
</Window.DataContext>
<StackPanel>
<GroupBox x:Name="grpDetails" Header="User Details" DataContext="{Binding CurrentUser, Mode=OneWay}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0">First Name:</Label>
<TextBox Grid.Column="1" Grid.Row="0" Style="{StaticResource TextBox}" Text="{Binding FirstName}"></TextBox>
<Label Grid.Column="0" Grid.Row="1">Surname:</Label>
<TextBox Grid.Column="1" Grid.Row="1" Style="{StaticResource TextBox}" Text="{Binding LastName}"></TextBox>
<Label Grid.Column="0" Grid.Row="2">Username:</Label>
<TextBox Grid.Column="1" Grid.Row="2" Style="{StaticResource TextBox}" Text="{Binding Username}"></TextBox>
<Label Grid.Column="0" Grid.Row="3">Password:</Label>
<PasswordBox Grid.Column="1" Grid.Row="3" Style="{StaticResource PasswordBox}"></PasswordBox>
<Label Grid.Column="0" Grid.Row="4">Confirm Password:</Label>
<PasswordBox Grid.Column="1" Grid.Row="4" Style="{StaticResource PasswordBox}"></PasswordBox>
</Grid>
</GroupBox>
<StackPanel Orientation="Horizontal">
<Button Style="{StaticResource Button}" Command="{Binding SaveCommand}" CommandParameter="{Binding CurrentUser}">Save</Button>
<Button Style="{StaticResource Button}">Cancel</Button>
</StackPanel>
<DataGrid x:Name="grdUsers" AutoGenerateColumns="False" CanUserAddRows="False" CanUserResizeRows="False"
Style="{StaticResource DataGrid}" ItemsSource="{Binding Users}" SelectedItem="{Binding CurrentUser, Mode=OneWayToSource}">
<DataGrid.Columns>
<DataGridTextColumn Header="Full Name" IsReadOnly="True" Binding="{Binding FullName}" Width="2*"></DataGridTextColumn>
<DataGridTextColumn Header="Username" IsReadOnly="True" Binding="{Binding Username}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
这个模型本身没有什么特别的(基类仅实现了INotifyPropertyChanged
接口并触发相关事件):
public class UserModel : PropertyChangedBase
{
private int _id;
public int Id
{
get { return _id; }
set
{
_id = value;
RaisePropertyChanged("Id");
}
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
RaisePropertyChanged("LastName");
RaisePropertyChanged("FullName");
}
}
private string _username;
public string Username
{
get { return _username; }
set
{
_username = value;
RaisePropertyChanged("Username");
}
}
public string FullName
{
get { return String.Format("{0} {1}", FirstName, LastName); }
}
}
视图模型(
IRemoteStore
提供对底层记录存储的访问):public class UserViewModel : PropertyChangedBase
{
private IRemoteStore _remoteStore = Bootstrapper.RemoteDataStore;
private ICommand _saveCmd;
public UserViewModel()
{
Users = new ObservableCollection<UserModel>();
foreach (var user in _remoteStore.GetUsers()) {
Users.Add(user);
}
_saveCmd = new SaveCommand<UserModel>((model) => {
Users[Users.IndexOf(Users.First(e => e.Id == model.Id))] = model;
});
}
public ICommand SaveCommand
{
get { return _saveCmd; }
}
public ObservableCollection<UserModel> Users { get; set; }
private UserModel _currentUser;
public UserModel CurrentUser
{
get { return _currentUser; }
set
{
_currentUser = value;
RaisePropertyChanged("CurrentUser");
}
}
}
为了完整起见,这是我保存ICommand
的实现(目前还没有实际持久化任何内容,因为我想先正确地使数据绑定工作):
public class SaveCommand<T> : ICommand
{
private readonly Action<T> _saved;
public SaveCommand(Action<T> saved)
{
_saved = saved;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_saved((T)parameter);
}
}
显然,我希望使用纯MVVM模式来实现这个。我已经尝试将
DataGrid
中的绑定设置为OneWay
,但这会导致网格中的更改不会反映出来(尽管新条目确实被添加)。我还查看了这篇 SO问题,该问题建议在ViewModel上使用“selected”属性。我的原始实现,如上所述,已经有了这样一个属性(称为“CurrentUser”),但是使用当前的绑定配置,网格仍会在用户进行更改时更新。
如果您能提供任何帮助,将不胜感激,因为我已经在解决这个问题上碰壁了几个小时。如果我漏掉了任何内容,请评论并我会更新帖子。谢谢。