我有一个相对简单的WPF应用程序。想法是向用户呈现项目列表;对于每个项目,都有一个复选框来选择/取消选择该项目。
我的代码,简化,看起来有点像这样:
class Thing { /* ... */ };
public static int SelectedCount { get; set; }
public class SelectableThing
{
private bool _selected;
public bool Selected {
get { return _selected; }
set { _selected = value; if (value) { SelectedCount++; } else { SelectedCount--; } }
}
public Thing thing { get; set; }
};
private ObservableCollection<SelectableThing> _selectableThings;
public Collection<SelectableThing> { get { return _selectableThings; } }
<DataGrid ItemSource="{Binding Path=SelectableThings}">
<DataGridCheckBoxColumn Binding="{Binding Selected}"/>
<DataGridTextColumn Binding="{Binding Thing.name}"/>
</DataGrid>
<Button Content="{Binding Path=SelectedTestCount}" Click="someFunc" />
因此,我们的想法是按钮内容应该显示所选测试的数量。这应该完成,因为无论何时设置SelectableThing.Selected,它都应该适当地增加/减少SelectedCount。
但是,据我所知,行为不起作用。无论选择/取消选择列表中的项目,按钮文本都显示“0”。
有什么想法吗?
答案 0 :(得分:3)
由于涉及多个视图模型类,因此这个问题有点毛茸茸。这是解决这个问题的代码。我唯一缺少的是,在你离开行之前,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
属性的setter中,当值更改时,请引发CanExecuteChanged
事件。
Command
上的Button
绑定会根据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内联:
this.SaveCommand = new DelegateCommand(Save,
o => SelectableThings.Any(t => t.IsSelected)
);
答案 1 :(得分:1)
将按钮的内容绑定到viewmodel中的字段,并在每次选择或取消选择其他项时触发该字段的OnChanged方法。将IsEnabled绑定到视图模型中的布尔字段,并根据需要将其设置为true / false以启用或禁用该按钮。