这个问题有点棘手,因为您涉及多个视图模型类。这是解决此问题的代码。我唯一缺少的是 DataGrid 在您离开该行之前似乎不会更新您的项目。
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding Path=SelectableThings}"
Grid.Row="0" Margin="6">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="IsSelected"
Binding="{Binding IsSelected}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="{Binding Path=SelectedTestCount}"
Command="{Binding SaveCommand}"
Grid.Row="1" Width="75" Height="23"
HorizontalAlignment="Right" Margin="0,0,6,6"/>
</Grid>
</Window>
事物类:
public class Thing : INotifyPropertyChanged
{
private readonly List<SelectableThing> selectableThings;
private DelegateCommand saveCommand;
public Thing(IEnumerable<SelectableThing> selectableThings)
{
this.selectableThings = new List<SelectableThing>(selectableThings);
this.SelectableThings =
new ObservableCollection<SelectableThing>(this.selectableThings);
this.SaveCommand = this.saveCommand = new DelegateCommand(
o => Save(),
o => SelectableThings.Any(t => t.IsSelected)
);
// Bind children to change event
foreach (var selectableThing in this.selectableThings)
{
selectableThing.PropertyChanged += SelectableThingChanged;
}
SelectableThings.CollectionChanged += SelectableThingsChanged;
}
public ObservableCollection<SelectableThing> SelectableThings
{
get;
private set;
}
public int SelectedTestCount
{
get { return SelectableThings.Where(t => t.IsSelected).Count(); }
}
public ICommand SaveCommand { get; private set; }
private void Save()
{
// Todo: Implement
}
private void SelectableThingChanged(object sender,
PropertyChangedEventArgs args)
{
if (args.PropertyName == "IsSelected")
{
RaisePropertyChanged("SelectedTestCount");
saveCommand.RaiseCanExecuteChanged();
}
}
private void SelectableThingsChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
foreach (SelectableThing selectableThing in
e.OldItems ?? new List<SelectableThing>())
{
selectableThing.PropertyChanged -= SelectableThingChanged;
RaisePropertyChanged("SelectedTestCount");
}
foreach (SelectableThing selectableThing in
e.NewItems ?? new List<SelectableThing>())
{
selectableThing.PropertyChanged += SelectableThingChanged;
RaisePropertyChanged("SelectedTestCount");
}
}
public void RaisePropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
SelectableThing 类:
public class SelectableThing : INotifyPropertyChanged
{
private string name;
private bool isSelected;
public SelectableThing(string name)
{
this.name = name;
}
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged("Name");
}
}
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
原答案:
将Command 绑定到ICommand。将您的CanExecute 设置在ICommand 上以在您的条件不满足时返回false。
在该IsSelected 属性的设置器中,当值更改时,引发CanExecuteChanged 事件。
Button 上的 Command 绑定会根据 CanExecute 的结果自动启用/禁用按钮。
有关如何执行此操作的更多信息,包括您可以在此处使用的ICommand 的实现,请参阅this mini-MVVM tutorial 我写了另一个问题。
要填写CanExecute 的实现,我会使用类似于Linq 的.Any 方法。那么你就不用费心去检查Count了,如果你发现有任何一项被检查了,可以提前终止循环。
例如:
this.SaveCommand = new DelegateCommand(Save, CanSave);
// ...
private void Save(object unusedArg)
{
// Todo: Implement
}
private bool CanSave(object unusedArg)
{
return SelectableThings.Any(t => t.IsSelected);
}
或者因为它很短,所以使用 lambda inline:
this.SaveCommand = new DelegateCommand(Save,
o => SelectableThings.Any(t => t.IsSelected)
);