2011-10-27 42 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

ボタンの内容をviewmodelのフィールドにバインドし、別の項目が選択または選択解除されるたびにそのフィールドの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)); 
     } 
    } 
} 

元回答:

バインドCommandからICommandです。あなたの条件が満たされていない場合を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); 
} 

それとも短いので、ラムダインライン使用:

this.SaveCommand = new DelegateCommand(Save, 
    o => SelectableThings.Any(t => t.IsSelected) 
    ); 
+0

'CanExecuteChanged'を' IsSelected'に基づいて変更することについてのビットは、子ビューモデルのコレクションを使用します。それぞれに対して 'INotifyPropertyChanged'を購読し、イベントを' this.SaveCommand.RaiseCanExecuteChanged() 'に転送することができます。もしあなたがこの部分を理解するのに困っていることがあれば教えてください。 –

+0

また、プロパティの名前を「IsSelected」にすることをお勧めします。ブール値(Is、Can、Hasなど)に名前を付けるときは、この規則に従ってください。 –

+0

あなたの意見を反映するために、元の質問を修正しました。あなたは私にあなたの考えを知らせることができますか? –

関連する問題