如何在WPF中启用/禁用按钮?

如何在WPF中启用/禁用按钮?,wpf,Wpf,我有一个相对简单的WPF应用程序。其思想是向用户呈现项目列表;对于每个项目,都有一个复选框用于选择/取消选择该项目 我的简化代码看起来有点像这样: class Thing { /* ... */ }; public static int SelectedCount { get; set; } public class SelectableThing { private bool _selected; public bool Selected {

我有一个相对简单的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" />
类事物{/*…*/};
公共静态int-SelectedCount{get;set;}
公共类可选择事物
{
选择私人布尔;
已选择公共布尔值{
获取{return\u selected;}
设置{u selected=value;如果(value){SelectedCount++;}否则{SelectedCount--;}
}
公共事物{get;set;}
};
私人可观察收集(可选择的事物);;
公共集合{get{return\u selectableThings;}
因此,这个想法是按钮内容应该显示所选测试的计数。这应该实现,因为无论何时设置SelectableThing.Selected,它都应该根据需要增加/减少SelectedCount

然而,据我所知,这种行为不起作用。无论选择/取消选择列表中的项目,按钮文本均显示“0”


有什么想法吗?

将按钮的内容绑定到viewmodel中的某个字段,并在每次选择或取消选择另一项时为该字段触发OnChanged方法。将IsEnabled绑定到视图模型中的布尔字段,并根据需要将其设置为true/false以启用或禁用按钮。

由于涉及多个视图模型类,因此此问题有点棘手。这里有一个破解代码来解决这个问题。我唯一缺少的是DataGrid似乎在您离开该行之前不会更新您的项目

XAML:

原始答案:

命令
绑定到
ICommand
。将
i命令上的
CanExecute
设置为在条件不满足时返回
false

在该
IsSelected
属性的setter中,当值更改时,引发
CanExecuteChanged
事件

按钮上的
命令
绑定
会根据
CanExecute
的结果自动启用/禁用按钮

有关如何执行此操作的更多信息,包括可以在此处使用的
ICommand
的实现,请参阅我撰写的另一个问题

为了填写
CanExecute
的实现,我会使用类似Linq的
。Any
方法。这样,您就不必费心检查
计数
,如果您发现任何项目都已检查,则可以提前终止循环

例如:

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)
    );

基于
IsSelected
更改提升
CanExecuteChanged
的部分可能很棘手,因为看起来您使用的是一组子视图模型。您可以在每个上订阅
INotifyPropertyChanged
,并将事件转发到
this.SaveCommand.raisecancecutechanged()
。如果您在计算此部分时遇到问题,请告诉我。另外请注意,我建议您将属性命名为
IsSelected
。在命名布尔值(Is、Can、Has等)时,应遵循此约定;你能告诉我你的想法吗?@StephenGross:虽然使用
静态
成员可能有效,但这是一种黑客行为。您不能支持持有该属性的任何类的多个实例(
Thing
?)。为这段代码编写单元测试也将更加困难。我也不确定WPF是否可以绑定到静态属性,或者
INotifyPropertyChanged
是否可以使用它。是的,我使用静态int的唯一原因是,否则您无法从SelectableThing.Selected.set()内部对其进行更改。您是使用viewmodels还是只使用代码隐藏?我已经修改了原始问题来尝试这一点,但可能是弄错了。。。
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));
        }
    }
}
this.SaveCommand = new DelegateCommand(Save, CanSave);

// ...

private void Save(object unusedArg)
{
    // Todo: Implement
}

private bool CanSave(object unusedArg)
{
    return SelectableThings.Any(t => t.IsSelected);
}
this.SaveCommand = new DelegateCommand(Save,
    o => SelectableThings.Any(t => t.IsSelected)
    );