2011-07-06 17 views
6

私はMVVM、VS 2008、および.NET 3.5 SP1を使用しています。アイテムのリストがあり、それぞれがIsSelectedプロパティを公開しています。 CheckBoxを追加して、リスト内のすべてのアイテムの選択/選択解除(各アイテムのIsSelectedプロパティの更新)を管理しました。 CheckBoxのバインドされたコントロールのPropertyChangedイベントが発生したときに、IsCheckedプロパティがビューで更新されていないことを除いて、すべてが機能しています。WPF CheckBoxバインディングが機能しないのはなぜですか?

<CheckBox 
    Command="{Binding SelectAllCommand}" 
    IsChecked="{Binding Path=AreAllSelected, Mode=OneWay}" 
    Content="Select/deselect all identified duplicates" 
    IsThreeState="True" /> 

マイVM:

public class MainViewModel : BaseViewModel 
{ 
    public MainViewModel(ListViewModel listVM) 
    { 
    ListVM = listVM; 
    ListVM.PropertyChanged += OnListVmChanged; 
    } 

    public ListViewModel ListVM { get; private set; } 
    public ICommand SelectAllCommand { get { return ListVM.SelectAllCommand; } } 

    public bool? AreAllSelected 
    { 
    get 
    { 
     if (ListVM == null) 
     return false; 

     return ListVM.AreAllSelected; 
    } 
    } 

    private void OnListVmChanged(object sender, PropertyChangedEventArgs e) 
    { 
    if (e.PropertyName == "AreAllSelected") 
     OnPropertyChanged("AreAllSelected"); 
    } 
} 

私はここSelectAllCommandまたは個々の項目選択の実装を示していないんだけど、関連していないようです。ユーザーがリスト内の単一の項目を選択するか(または問題のCheckBoxをクリックしてすべての項目を選択/選択解除する)、OnPropertyChanged( "AreAllSelected")コード行が実行され、デバッガでのトレースが確認できますPropertyChangedイベントは購読されており、期待どおりに起動します。しかし、AreAllSelectedプロパティのgetは、ビューが実際にレンダリングされたときに一度だけ実行されます。 Visual Studioの出力ウィンドウはデータバインディングエラーを報告しないので、私が知る限り、CheckBoxのIsSelectedプロパティは正しくバインドされています。

私はボタンが付いたチェックボックス交換する場合:

<Button Content="{Binding SelectAllText}" Command="{Binding SelectAllCommand}"/> 

をしてVMを更新:

... 

public string SelectAllText 
{ 
    get 
    { 
    var msg = "Select All"; 
    if (ListVM != null && ListVM.AreAllSelected != null && ListVM.AreAllSelected.Value) 
     msg = "Deselect All"; 

    return msg; 
    } 
} 

... 

private void OnListVmChanged(object sender, PropertyChangedEventArgs e) 
{ 
    if (e.PropertyName == "AreAllSelected") 
    OnPropertyChanged("SelectAllText"); 
} 

すべてが期待通りに動作します - すべての項目が選択されているとして、ボタンのテキストがdesected /更新されます。 CheckBoxのIsSelectedプロパティにバインドされていないものがありますか?

ありがとうございました!

答えて

5

問題が見つかりました。 IsCheckedでOneWayバインディングを使用しているWPF 3.0にバグが存在し、バインディングが削除されているようです。支援のためにthis postのおかげで、バグがWPF 4.0で修正されたように聞こえます

再現するには、新しいWPFプロジェクトを作成してください。

using System; 
using System.ComponentModel; 
using System.Windows.Input; 

namespace Foo 
{ 
    public class FooViewModel : INotifyPropertyChanged 
    { 
    private bool? _isCheckedState = true; 

    public FooViewModel() 
    { 
     ChangeStateCommand = new MyCmd(ChangeState); 
    } 

    public bool? IsCheckedState 
    { 
     get { return _isCheckedState; } 
    } 

    public ICommand ChangeStateCommand { get; private set; } 

    private void ChangeState() 
    { 
     switch (_isCheckedState) 
     { 
     case null: 
      _isCheckedState = true; 
      break; 
     default: 
      _isCheckedState = null; 
      break; 
     } 

     OnPropertyChanged("IsCheckedState"); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    protected void OnPropertyChanged(string propertyName) 
    { 
     var changed = PropertyChanged; 
     if (changed != null) 
     changed(this, new PropertyChangedEventArgs(propertyName)); 
    } 
    } 

    public class MyCmd : ICommand 
    { 
    private readonly Action _execute; 
    public event EventHandler CanExecuteChanged; 

    public MyCmd(Action execute) 
    { 
     _execute = execute; 
    } 

    public void Execute(object parameter) 
    { 
     _execute(); 
    } 

    public bool CanExecute(object parameter) 
    { 
     return true; 
    } 
    } 
} 

修正Window1.xaml.cs:

using System.Windows; 
using System.Windows.Controls.Primitives; 

namespace Foo 
{ 
    public partial class Window1 
    { 
    public Window1() 
    { 
     InitializeComponent(); 
    } 

    private void OnClick(object sender, RoutedEventArgs e) 
    { 
     var bindingExpression = MyCheckBox.GetBindingExpression(ToggleButton.IsCheckedProperty); 
     if (bindingExpression == null) 
     MessageBox.Show("IsChecked property is not bound!"); 
    } 
    } 
} 

修正Window1.xaml:ボタン上の

<Window 
    x:Class="Foo.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:vm="clr-namespace:Foo" 
    Title="Window1" 
    Height="200" 
    Width="200" 
    > 

    <Window.DataContext> 
    <vm:FooViewModel /> 
    </Window.DataContext> 

    <StackPanel> 
    <CheckBox 
     x:Name="MyCheckBox" 
     Command="{Binding ChangeStateCommand}" 
     IsChecked="{Binding Path=IsCheckedState, Mode=OneWay}" 
     Content="Foo" 
     IsThreeState="True" 
     Click="OnClick"/> 
    <Button Command="{Binding ChangeStateCommand}" Click="OnClick" Content="Change State"/> 
    </StackPanel> 
</Window> 

クリック数回

FooViewModel.csを追加します。 trueとnull(falseではない)の間のCheckBoxの状態トグルを参照してください。しかし、CheckBoxをクリックすると、バインディングがIsCheckedプロパティから削除されていることがわかります。

回避策:

更新にisChecked双方向であることを結合して、明示的であることをそのUpdateSourceTriggerを設定します。それはもはや読み取り専用ですので

IsChecked="{Binding Path=IsCheckedState, Mode=TwoWay, UpdateSourceTrigger=Explicit}" 

とはバウンドプロパティを更新しません:

public bool? IsCheckedState 
{ 
    get { return _isCheckedState; } 
    set { } 
} 
関連する問題