Question

I'm using this ObservableCollection-Class within my Project: Link
I want to Bind a RibbonMenuButton to a ObservableDictionary<string,bool>:

<r:RibbonMenuButton ItemsSource="{Binding MyDictionary}">
    <r:RibbonMenuButton.ItemContainerStyle>
        <Style TargetType="{x:Type r:RibbonMenuItem}">
            <Setter Property="IsCheckable" Value="true"/>
            <Setter Property="Header" Value="{Binding Path=Key}"/>
            <Setter Property="IsChecked" Value="{Binding Path=Value}"/>
        </style>
    </r:RibbonMenuButton.ItemContainerStyle>
</r:RibbonMenuButton>

But I get exceptions because the Value-Properties of the internal IDictionary-KeyValuePairs are readonly. Any Idea how to solve this?

I thought about something like:

<Setter Property="IsChecked" Value="{Binding Source=MyDictionary[{Binding Path=Key}]}"/>

But this won't work 'cause of {Binding} in {Binding}...

Was it helpful?

Solution

This doesn't work, because your dictionary isn't treated as a dictionary but as an IEnumerable<KeyValuePair<string, bool>>. So each RibbonMenuItem is bound to a KeyValuePair<string, bool> with readonly properties Key and Value.
You can do two one things:

1. Use an ObservableCollection<Tuple<string, bool>> instead of the dictionary and bind IsChecked to Item2.
2. Create a little helper class that contains a IsChecked property and change your dictionary to contain that class as the value and bind IsChecked to Value.IsChecked.

I would go with answer two, because the needed changes and possible side effects are smaller.
My answer assumes that you want to have a two way binding on IsChecked. If not, go with the answer of slugster.

OTHER TIPS

WPF binding is two-way by default. Make it one-way and see if that solves your issue.

<r:RibbonMenuButton ItemsSource="{Binding MyDictionary}">
    <r:RibbonMenuButton.ItemContainerStyle>
        <Style TargetType="{x:Type r:RibbonMenuItem}">
            <Setter Property="IsCheckable" Value="true"/>
            <Setter Property="Header" Value="{Binding Key, Mode=OneWay}"/>
            <Setter Property="IsChecked" Value="{Binding Value, Mode=OneWay}"/>
        </style>
    </r:RibbonMenuButton.ItemContainerStyle>
</r:RibbonMenuButton>

Here is a reference for you: MSDN Windows Presentation Foundation Data Binding: Part 1 (specifically check the section Binding Mode close to the bottom of the page)

If You want to bind MenuItems to Dictionary<string, bool> without using a helper class, like the accepted answer suggests, here is the minimal-change solution (no need to add anything else):

  • define a Click event inside the ItemContainerStyle whose ClickEventHandler will update the dicitonary.
  • declare a dictionary and initialize it inside the UserControl's / Window's constructor

In code:

MainWindow.xaml:

<MenuItem Header="_My settings" ItemsSource="{Binding MySettings}">
    <MenuItem.ItemContainerStyle>
      <Style TargetType="{x:Type MenuItem}">
        <Setter Property="IsCheckable" Value="true"/>
        <Setter Property="Header" Value="{Binding Key, Mode=OneWay}"/>
        <Setter Property="IsChecked" Value="{Binding Value, Mode=OneWay}"/>
        <!-- this is the main line of code -->
        <EventSetter Event="Click" Handler="MySettings_ItemClick"/>
      </Style>
    </MenuItem.ItemContainerStyle>
</MenuItem>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    // properties...

    // Declaration of the dictionary
    public Dictionary<string, bool> MySettings{ get; set; }

    public MainWindow()
    {
        InitializeComponent();
        // Initialize the dictionary
        MySettings = new Dictionary<string, bool>()
        {
            { "SettingOne", true}
            // Other pairs..
        };
    }
    // other things..

    // ClickEvent hanlder
    private void MySettings_ItemClick(object sender, RoutedEventArgs e)
    {
        MenuItem clickedItem = (sender as MenuItem);
        MySettings[clickedItem.Header as string] = clickedItem.IsChecked;
    }
} // end of MainWindow class

That's it! You're all set!

Credits to slugster and his answer for XAML code for OneWay binding :)

As a general solution to this problem of binding to dictionaries I created an UpdateableKeyValuePair and return that instaed of the usual KeyValuePair. Here is my class:

   public class UpdateableKeyValuePair<TKey,TValue>
      {
      private IDictionary<TKey, TValue> _owner;
      private TKey _key;
      public UpdateableKeyValuePair(IDictionary<TKey, TValue> Owner, TKey Key_)
         {
         _owner = Owner;
         _key = Key_;
         }

      public TKey Key
         {
         get
            {
            return _key;
            }
         }

      public TValue Value
        {
        get
          {
          return _owner[_key];
          }
       set
         {
          _owner[_key] = value;
         }
      }
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top