1

I have a group of ribbon toggle buttons inside of the same container in my XAML like this:

<ribbon:RibbonGroup Header="Layouts">
    <ribbon:RibbonToggleButton Label="One"
        IsChecked="{Binding PaneManager.Layout,
                            Converter={StaticResource EnumToBooleanConverter},
                            ConverterParameter={s:Static windows:Layouts.One}}"/>
    <ribbon:RibbonToggleButton Label="Two Vertical"
        IsChecked="{Binding PaneManager.Layout,
                            Converter={StaticResource EnumToBooleanConverter},
                            ConverterParameter={s:Static windows:Layouts.TwoVertical}}"/>
    <!-- etc. for 2 horizontal and 4 panes -->
</ribbon:RibbonGroup>

I'm using the same EnumToBooleanConverter outlined in this answer:

public class EnumToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value.Equals(parameter);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value.Equals(true) ? parameter : Binding.DoNothing;
    }
}

The problem is that when a user clicks the toggle button that's already selected, the toggle button happily turns itself off--even though the bound value matches that toggle button. When tracing through the code, what happens is that the ConvertBack method is called and it returns Binding.DoNothing. Afterwards, Convert is not called to reassign the value. When ConvertBack returns a value (i.e. it is clicked a second time and IsChecked is true again), the Convert method is called to reassign the value.

If I change the return type on false to be DependencyObject.UnsetValue, the toggle button is still turned off but now it has a red outline.

How do I force WPF to re-evaluate the bound value so that the toggle button stays on?

Community
  • 1
  • 1
Berin Loritsch
  • 11,400
  • 4
  • 30
  • 57
  • What is the data type of `PaneManager.Layout`? the `ConvertBack` should return a valid value of that data type. – King King Oct 14 '15 at 13:29
  • Rule #1: Always define `..Parameter` first. Regarding problem: you can't use single parameter converter, because you need to pass **current** value of enum to convert `IsChecked` back (I assume you are using `[Flag]` enum). I'd use command and one-way converter myself. You may decide to use MultiValueConverter. – Sinatr Oct 14 '15 at 13:30
  • @KingKing It's an application specific `enum`. When toggling between the toggle buttons the converter supplied works like a charm. – Berin Loritsch Oct 14 '15 at 14:15
  • @Sinatr It's not a [Flag] enum. Just a standard mutually exclusive value. I'm not experiencing any issues with the `CoonverterParameter` being defined second. Command seems so over-architected since everything that needs to be done is handled in the DependencyProperty changed callback. – Berin Loritsch Oct 14 '15 at 14:18
  • I thought you want to toggle bits. You are using `HasFlag` for unknown reasons, it could be a simple check `(Enum)value == (Enum)parameter`. `PaneManager.Layout` don't have to be a `DependencyProperty`, `IsChecked` can be bound to a normal property. – Sinatr Oct 14 '15 at 14:35
  • I fixed the code to what we have (using .Equals). I copy pasted the snippet from the other answer. And yes, I know it can be a normal property, but this is what we have since the binding is two way by design. – Berin Loritsch Oct 14 '15 at 15:11

1 Answers1

0

OK, it seems I have to control whether the toggle button IsHitTestVisible to make it behave properly. If I change my toggle buttons to look like this:

<ribbon:RibbonGroup Header="Layouts">
    <ribbon:RibbonToggleButton Label="One"
        IsChecked="{Binding PaneManager.Layout,
                            Converter={StaticResource EnumToBooleanConverter},
                            ConverterParameter={s:Static windows:Layouts.One}}"
        IsHitTestVisible="{Binding IsChecked,
                                   RelativeSource={RelativeSource Self},
                                   Converter={StaticResource InverseBooleanConverter}}"/>

    <ribbon:RibbonToggleButton Label="Two Vertical"
        IsChecked="{Binding PaneManager.Layout,
                            Converter={StaticResource EnumToBooleanConverter},
                            ConverterParameter={s:Static windows:Layouts.TwoVertical}}"
        IsHitTestVisible="{Binding IsChecked,
                                   RelativeSource={RelativeSource Self},
                                   Converter={StaticResource InverseBooleanConverter}}"/>
    <!-- etc. for 2 horizontal and 4 panes -->
</ribbon:RibbonGroup>

And then supply the following converter:

public class InverseBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return !(bool)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return !(bool)value;
    }
}

It feels like a hack, but it does prevent toggling back off. I am seriously open to more elegant solutions.

Berin Loritsch
  • 11,400
  • 4
  • 30
  • 57