Почему мой WPF CheckBox Binding не работает? - PullRequest
7 голосов
/ 07 июля 2011

Я использую MVVM, VS 2008 и .NET 3.5 SP1. У меня есть список элементов, каждый из которых выставляет свойство IsSelected. Я добавил флажок для управления выбором / отменой выбора всех элементов в списке (обновляя свойство IsSelected каждого элемента). Все работает, за исключением того, что свойство IsChecked не обновляется в представлении, когда происходит событие PropertyChanged для привязанного элемента управления CheckBox.

<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 или выбор отдельных элементов здесь, но это, похоже, не актуально. Когда пользователь выбирает один элемент в списке (или щелкает проблемный флажок для выбора / отмены выбора всех элементов), я убедился, что строка кода OnPropertyChanged ("AreAllSelected") выполняется и отслеживание в отладчике может видеть событие PropertyChanged подписано и запускается, как и ожидалось. Но получение свойства AreAllSelected выполняется только один раз - когда представление фактически отображается. Окно вывода Visual Studio не сообщает об ошибках привязки данных, поэтому, насколько я могу судить, свойство IsSelected в CheckBox правильно связано.

Если я заменю 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");
}

все работает как положено - текст кнопки обновляется по мере того, как все элементы выделены / отменены. Что-то мне не хватает в привязке в свойстве IsBelected CheckBox?

Спасибо за любую помощь!

1 Ответ

6 голосов
/ 07 июля 2011

Я нашел проблему.Кажется, в WPF 3.0 существовала ошибка с привязками OneWay на IsChecked, приводившая к удалению привязки.Спасибо этому посту за помощь, похоже, ошибка была исправлена ​​в 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 переключается между истиной и нулем (не ложь).Но нажмите на флажок, и вы увидите, что привязка удалена из свойства IsChecked.

Обходной путь:

Обновите привязку IsChecked, указав TwoWay, и установите ееUpdateSourceTrigger должен быть явным:

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

и обновить привязанное свойство, чтобы оно больше не было доступно только для чтения:

public bool? IsCheckedState
{
  get { return _isCheckedState; }
  set { }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...