2011-10-27 82 views
3

我有一個相對簡單的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」。

任何想法?

+0

您正在使用的ViewModels或者只是代碼隱藏? – Yatrix

+0

我正在使用viewmodels。 –

回答

1

將按鈕的內容綁定到視圖模型中的某個字段,並在每次選擇或取消選擇其他項目時觸發該字段的OnChanged方法。將IsEnabled綁定到視圖模型中的布爾字段,並根據需要將其設置爲true/false以啓用或禁用按鈕。

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

原始回答:

綁定CommandICommand。當您的狀況不符合時,將您的CanExecute設置爲ICommand以返回false

在設置者IsSelected屬性中,當值發生變化時,引發CanExecuteChanged事件。

Command綁定在Button自動啓用/禁用基於CanExecute的結果的按鈕。

有關如何做到這一點,包括ICommand的實現,你可以在這裏使用,請參閱this mini-MVVM tutorial我寫了另一個問題的更多信息。

要填寫的CanExecute的implementaiton,我會使用類似的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) 
    ); 
+0

有關基於IsSelected改變'CanExecuteChanged'的一點可能會很棘手,因爲它看起來像你使用一系列子視圖模型。你可以訂閱每個'INotifyPropertyChanged',並將事件轉發到'this.SaveCommand.RaiseCanExecuteChanged()'。讓我知道你是否有麻煩找出這部分。 –

+0

另請注意,我建議你命名屬性IsSelected。命名布爾值(Is,Can,Has等)時,應該遵循這個約定。 –

+0

我修改了原始問題以反映您的輸入;你能讓我知道你的想法嗎? –