OneWay binding for ToggleButton's IsChecked property in WPF

M. Dudley picture M. Dudley · Sep 22, 2010 · Viewed 9.2k times · Source

I have a ToggleButton with its IsChecked property bound to a property using a OneWay binding.

<ToggleButton
    Command="{Binding Path=SomeCommand}"
    IsChecked="{Binding Path=SomeProperty, Mode=OneWay}" />

The SomeCommand toggles the boolean SomeProperty value, and a PropertyChanged event is raised for SomeProperty.

If I change SomeProperty in my viewmodel the ToggleButton depresses correctly. However if I click the ToggleButton the binding seems to get lost and the button no longer gets checked according to the value of SomeProperty. Any ideas on how to fix this problem?

Answer

Alex_P picture Alex_P · Apr 28, 2011

There is a simple and elegant way to solve the original poster's problem - replacing IsChecked property of the ToggleButton with an attachable property that will set IsChecked of the button in its change handler:

namespace TBFix
{
  public class TBExtender
  {
    public static readonly DependencyProperty IsCheckedProperty =
      DependencyProperty.RegisterAttached("IsChecked", 
                                          typeof(bool),
                                          typeof(TBExtender),
                                          new PropertyMetadata(OnChanged));

    public static bool GetIsChecked(DependencyObject obj)
    {
      return (bool)obj.GetValue(IsCheckedProperty);
    }
    public static void SetIsChecked(DependencyObject obj, bool value)
    {
      obj.SetValue(IsCheckedProperty, value);
    }

    private static void OnChanged(DependencyObject o,
                                  DependencyPropertyChangedEventArgs args)
    {
      ToggleButton tb = o as ToggleButton;
      if (null != tb)
        tb.IsChecked = (bool)args.NewValue;
    }
  }
}

XAML then will look like this:

<ToggleButton Command="{Binding Path=SomeCommand}"
              TBFix:TBExtender.IsChecked="{Binding Path=SomeProperty,
                                                   Mode=OneWay}" />

EDIT: The OP solution does not work, because when the button is pressed the IsChecked property is set in the code (this is the way MS implemented ToggleButton control) - setting the property removes the binding from it and so it stops working.

By using attached property we can overcome this problem because it is never assigned a value in code and so the bindings stays intact.