2011-07-06 45 views
6

我正在使用MVVM,VS 2008和.NET 3.5 SP1。我有一个项目列表,每个项目都暴露一个IsSelected属性。我添加了一个CheckBox来管理列表中所有项目的选择/取消选择(更新每个项目的IsSelected属性)。一切正常,除非在检查框的绑定控件触发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属性上的Binding的内容?

感谢您的帮助!

回答

5

我发现了这个问题。这似乎是一个存在于WPF 3.0中的错误,在IsChecked上使用OneWay绑定导致绑定被删除。感谢this post的帮助,这听起来像是在WPF 4.0中修正了错误。

重现,创建一个新的WPF项目。

添加FooViewModel.cs:

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> 

点击按钮几次并查看CheckBox的状态在true和null之间切换(而非false)。但点击复选框,你会看到绑定已从IsChecked属性中删除。

解决方法:

更新器isChecked结合是双向并设置其UpdateSourceTrigger是明确的:

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

和更新绑定属性,因此它不再是只读:

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