【问题标题】:Why is my WPF CheckBox Binding not working?为什么我的 WPF CheckBox 绑定不起作用?
【发布时间】:2011-09-29 23:19:27
【问题描述】:

我正在使用 MVVM、VS 2008 和 .NET 3.5 SP1。我有一个项目列表,每个项目都公开一个 IsSelected 属性。我添加了一个复选框来管理列表中所有项目的选择/取消选择(更新每个项目的 IsSelected 属性)。当为 CheckBox 的绑定控件触发 PropertyChanged 事件时,除了 IsChecked 属性未在视图中更新外,一切正常。

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

我的虚拟机:

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 属性已正确绑定。

如果我用按钮替换 CheckBox:

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

并更新虚拟机:

...

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

一切都按预期工作 - 按钮的文本会随着所有项目的选择/分离而更新。 CheckBox 的 IsSelected 属性上的绑定有什么我遗漏的吗?

感谢您的帮助!

【问题讨论】:

    标签: c# wpf data-binding mvvm


    【解决方案1】:

    我发现了问题。似乎 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)之间切换。但是点击 CheckBox 会看到 Binding 从 IsChecked 属性中移除了。

    解决方法:

    将 IsChecked 绑定更新为 TwoWay 并将其 UpdateSourceTrigger 设置为显式:

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

    并更新绑定的属性,使其不再是只读的:

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

    【讨论】:

      猜你喜欢
      • 2011-01-16
      • 2013-04-12
      • 2016-04-10
      • 2019-11-13
      • 1970-01-01
      • 2022-11-20
      • 1970-01-01
      • 2017-10-16
      • 1970-01-01
      相关资源
      最近更新 更多