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

时间:2011-10-27 19:29:34

标签: wpf

我有一个相对简单的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”。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

由于涉及多个视图模型类,因此这个问题有点毛茸茸。这是解决这个问题的代码。我唯一缺少的是,在你离开行之前,DataGrid似乎似乎没有更新你的物品。

At start, before any edits.  The save button is disabled, and the count is zero After some edits.  The save button is enabled, and the count is updated

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以启用或禁用该按钮。