Why is my WPF CheckBox Binding not working?

I am using MVVM, VS 2008 and .NET 3.5 SP1. I have a list of elements, each of which exposes the IsSelected property. I added a CheckBox to control the selection / deselection of all items in the list (updating each IsSelected property). Everything works, except that the IsChecked property is not updated in the view when the PropertyChanged event is fired for the associated CheckBox control.

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

My virtual machine:

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

I am not showing an implementation of SelectAllCommand or individual elements here, but that does not seem to matter. When the user selects one item in the list (or clicks on the CheckBox problem to select / remove all items), I checked that the OnPropertyChanged ("AreAllSelected") line of code executes and monitors in the debugger, can see the PropertyChanged event is signed and fires as expected . But the getAllSelected get property is only executed once - when the view is actually displayed. The Visual Studio Output window does not report any data binding errors, so from what I can tell, the CheckBox IsSelected property is correctly bound.

If I replace CheckBox with a button:

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

and update the virtual machine:

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

everything works as expected - the button text is updated as all elements are selected / disabled. Is there something I am missing in binding to the CheckBox IsSelected property?

Thanks for any help!

+6
source share
1 answer

I found a problem. It seems that in WPF 3.0 there is an error with OneWay bindings on IsChecked, which leads to the removal of the binding. Thanks to this post for help, it looks like the bug was fixed in WPF 4.0

To reproduce, create a new WPF project.

Add 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; } } } 

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

Change 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> 

Click the button several times and see that the state of the CheckBox switches between true and null (not false). But click on CheckBox and you will see that the binding is removed from the IsChecked property.

Workaround:

Update IsChecked binding to TwoWay and set its UpdateSourceTrigger explicitly:

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

and update the related property so that it is no longer read-only:

 public bool? IsCheckedState { get { return _isCheckedState; } set { } } 
+5
source

Source: https://habr.com/ru/post/892168/


All Articles