WPF MVVM - 监视对绑定对象的更改(例如,如果 IsDirty 则启用保存)
WPF MVVM - Monitor Changes to Bound Object (e.g. Enable Save if IsDirty)
在 WPF 中,使用 MVVM 模式(并使用 MVVMLight 工具包),我有一个显示产品可编辑字段的用户控件。此 UC 绑定到从列出所有产品的 ComboBox 中选择的值。
一切正常,但在我的产品 class 中,我有一个名为 IsDirty 的布尔字段,当产品上的任何 属性 更新时,该字段设置为 true。 我希望只要所选产品(在用户控件中)的 IsDirty 属性 为真,我就可以启用主视图中的保存按钮。 我想不通如何让 IsSaveEnabled 属性 "watch" 用于 SelectedProduct 的更改(基本上当它变脏时)。
我能够通过使用事件处理程序来完成此操作,但是它将一个事件附加到每个选定的产品并且它看起来又脏又笨重。这是我在如何正确监视绑定对象的更改方面缺少的东西吗?
Products.xaml (部分)
<Grid DataContext="{Binding Source={StaticResource ProductsViewModel}}">
<StackPanel>
<ComboBox x:Name="ProductsComboBox"
ItemsSource="{Binding Path=ProductList}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedProduct}" />
<uc:Product DataContext="{Binding SelectedItem, ElementName=ProductsComboBox}" />
<Button Content="Save" IsEnabled="{Binding IsSaveEnabled}" Click="Button_Click" />
</StackPanel>
</Grid>
ProductsViewModel.cs (部分)
public bool IsSaveEnabled
{
get { return SelectedProduct.IsDirty; }
}
public ProductViewModel SelectedProduct
{
get { return _selectedProduct; }
set {
// I can use the next line, but it seems really clunky and creates a ton of event listeners
// value.PropertyChanged += SelectedProduct_PropertyChanged;
Set(() => SelectedProduct, ref _selectedProduct, value);
}
}
// This is the event handler that seems bad form and a duplication of functionality that is already in the Product class
//void SelectedProduct_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
// {
// SelectedProperty.IsDirty = true;
// RaisePropertyChanged(() => IsSaveEnabled);
// }
我会这样做:
public class ProductsViewModel
{
private bool _isSaveEnabled;
public bool IsSaveEnabled
{
get { return _isSaveEnabled; }
private set
{
_isSaveEnabled = value;
RaisePropertyChanged();
}
}
public ProductViewModel SelectedProduct
{
get { return _selectedProduct; }
set
{
if (value != _selectedProduct)
{
// unsubscribe from old selected product changes
//
_selectedProduct.PropertyChanged -= OnPropertyChanged;
// subscribe to new selected product changes
//
value.PropertyChanged += OnPropertyChanged;
_selectedProduct = value;
RaisePropertyChanged();
}
}
}
private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
SelectedProperty.IsDirty = true;
IsSaveEnabled = SelectedProperty.IsDirty;
}
}
您看到这么多事件处理程序的原因是每次所选产品更改时您都在订阅新的事件处理程序。您只想为一个人做这件事,所以请确保在订阅新的之前取消订阅旧的。此外,无需为 IsSaveEnabled 执行 RaisePropertyChanged,只需在其 setter.
中引发事件
您可能应该改用命令:
public static RoutedCommand Command_Save = new RoutedCommand ( );
void Command_Save_Executed ( object sender, ExecutedRoutedEventArgs e )
{
// save the product
}
void Command_Save_CanExecute ( object sender, CanExecuteRoutedEventArgs e )
{
e.CanExecute = SelectedProduct.IsDirty;
}
XAML:
<Button Content="Save" Command="{x:Static MY:ProductsControl.Command_Save }" />
并将其绑定到控件(在 ctor 或 xaml 中):
this.CommandBindings.Add( new CommandBinding( Command_SaveAs,
Command_SaveAs_Executed,
Command_SaveAs_CanExecute ) );
命令基础结构将调用您的 CanExecute 事件处理程序来 disable/enable 按钮。
在 WPF 中,使用 MVVM 模式(并使用 MVVMLight 工具包),我有一个显示产品可编辑字段的用户控件。此 UC 绑定到从列出所有产品的 ComboBox 中选择的值。
一切正常,但在我的产品 class 中,我有一个名为 IsDirty 的布尔字段,当产品上的任何 属性 更新时,该字段设置为 true。 我希望只要所选产品(在用户控件中)的 IsDirty 属性 为真,我就可以启用主视图中的保存按钮。 我想不通如何让 IsSaveEnabled 属性 "watch" 用于 SelectedProduct 的更改(基本上当它变脏时)。
我能够通过使用事件处理程序来完成此操作,但是它将一个事件附加到每个选定的产品并且它看起来又脏又笨重。这是我在如何正确监视绑定对象的更改方面缺少的东西吗?
Products.xaml (部分)
<Grid DataContext="{Binding Source={StaticResource ProductsViewModel}}">
<StackPanel>
<ComboBox x:Name="ProductsComboBox"
ItemsSource="{Binding Path=ProductList}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedProduct}" />
<uc:Product DataContext="{Binding SelectedItem, ElementName=ProductsComboBox}" />
<Button Content="Save" IsEnabled="{Binding IsSaveEnabled}" Click="Button_Click" />
</StackPanel>
</Grid>
ProductsViewModel.cs (部分)
public bool IsSaveEnabled
{
get { return SelectedProduct.IsDirty; }
}
public ProductViewModel SelectedProduct
{
get { return _selectedProduct; }
set {
// I can use the next line, but it seems really clunky and creates a ton of event listeners
// value.PropertyChanged += SelectedProduct_PropertyChanged;
Set(() => SelectedProduct, ref _selectedProduct, value);
}
}
// This is the event handler that seems bad form and a duplication of functionality that is already in the Product class
//void SelectedProduct_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
// {
// SelectedProperty.IsDirty = true;
// RaisePropertyChanged(() => IsSaveEnabled);
// }
我会这样做:
public class ProductsViewModel
{
private bool _isSaveEnabled;
public bool IsSaveEnabled
{
get { return _isSaveEnabled; }
private set
{
_isSaveEnabled = value;
RaisePropertyChanged();
}
}
public ProductViewModel SelectedProduct
{
get { return _selectedProduct; }
set
{
if (value != _selectedProduct)
{
// unsubscribe from old selected product changes
//
_selectedProduct.PropertyChanged -= OnPropertyChanged;
// subscribe to new selected product changes
//
value.PropertyChanged += OnPropertyChanged;
_selectedProduct = value;
RaisePropertyChanged();
}
}
}
private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
SelectedProperty.IsDirty = true;
IsSaveEnabled = SelectedProperty.IsDirty;
}
}
您看到这么多事件处理程序的原因是每次所选产品更改时您都在订阅新的事件处理程序。您只想为一个人做这件事,所以请确保在订阅新的之前取消订阅旧的。此外,无需为 IsSaveEnabled 执行 RaisePropertyChanged,只需在其 setter.
中引发事件您可能应该改用命令:
public static RoutedCommand Command_Save = new RoutedCommand ( );
void Command_Save_Executed ( object sender, ExecutedRoutedEventArgs e )
{
// save the product
}
void Command_Save_CanExecute ( object sender, CanExecuteRoutedEventArgs e )
{
e.CanExecute = SelectedProduct.IsDirty;
}
XAML:
<Button Content="Save" Command="{x:Static MY:ProductsControl.Command_Save }" />
并将其绑定到控件(在 ctor 或 xaml 中):
this.CommandBindings.Add( new CommandBinding( Command_SaveAs,
Command_SaveAs_Executed,
Command_SaveAs_CanExecute ) );
命令基础结构将调用您的 CanExecute 事件处理程序来 disable/enable 按钮。