WPF: Menu Items only bind command parameters once
Asked Answered
B

1

3

Ive noticed this a couple of times when using menus with commands, they are not very dynamic, check this out. I am creating a menu from a collection of colours, I use it to colour a column in a datagrid. Anyway when i first bring up the menu (its a context menu) the command parameter binding happens and it binds to the column that the context menu was opened on. However the next time i bring it up it seems wpf caches the menu and it doesnt rebind the command parameter. so i can set the colour only on the initial column that the context menu appeared on.

I have got around this situation in the past by making the menu totally dynamic and destroying the collection when the menu closed and forcing a rebuild the next time it opened, i dont like this hack. anyone got a better way?

    <MenuItem
       Header="Colour"
       ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:ResultEditorGrid}}, Path=ColumnColourCollection}"
       ItemTemplate="{StaticResource colourHeader}" >
       <MenuItem.Icon>
          <Image
             Source="{StaticResource ColumnShowIcon16}" />
       </MenuItem.Icon>
       <MenuItem.ItemContainerStyle>
          <Style
             TargetType="MenuItem"
             BasedOn="{StaticResource systemMenuItemStyle}">
             <!--Warning dont change the order of the following two setters
                                otherwise the command parameter gets set after the command fires,
                                not mush use eh?-->
             <Setter
                Property="CommandParameter">
                <Setter.Value>
                   <MultiBinding>
                      <MultiBinding.Converter>
                         <local:ColumnAndColourMultiConverter/>
                      </MultiBinding.Converter>
                      <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGridColumnHeader}}" Path="Column"/>
                      <Binding Path="."/>
                   </MultiBinding>
                </Setter.Value>
             </Setter>
             <Setter
                Property="Command"
                Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:ResultEditorGrid}}, Path=ColourColumnCommand}" />
          </Style>
       </MenuItem.ItemContainerStyle>
    </MenuItem>
Baccivorous answered 10/6, 2010 at 2:3 Comment(0)
A
1

The problem is that ContextMenu's are apparently the root of their own visual tree I read somewhere that it takes the datacontext its parent, but only once on loading, so if the parents datacontext changes the menuitems does not. (unfortunately I can't find a link for that right not)

I have encountered this problem before, and what I did was use Josh Smith's Virtual Branch Pattern. It's fairly technical but the article helped me understand really well what was going on with this visual tree nonsense.

Essentially you create this bridge that binds to the view's datacontext. The bridge is created as a static resource, allowing you to bind to it from the context menu even if it is outside the visual tree.

Add this to your xaml:

<Window.Resources>
   <!-- This is the "root node" in the virtual branch
   attached to the logical tree. It has its
   DataContext set by the Binding applied to the
   Window's DataContext property. -->
   <FrameworkElement x:Key="DataContextBridge" />
</Window.Resources>

<Window.DataContext>
   <!-- This Binding sets the DataContext on the "root node"
   of the virtual logical tree branch.  This Binding
   must be applied to the DataContext of the element
   which is actually assigned the data context value. -->
   <Binding
    Mode="OneWayToSource"
    Path="DataContext"
    Source="{StaticResource DataContextBridge}"
   />
</Window.DataContext>

This is the bridge I spoke of. It takes the datacontext and __pushes it_ to to the bridges datacontext, which is (as I said before) a static resource.

Then you simply this to the contextmenu's datacontext:

 DataContext="{Binding
               Source={StaticResource DataContextBridge},
               Path=DataContext}"

Now throw away all the relative pathing etc and use regular binding inside the menu items, and you should be fine. The datacontext will update as usual.

Just one note:

You will obviously have to have some property in the datacontext to discern which command to use, but i'm sure you can figure it out. This solution just deals with the way contextmenu's dont update

Amias answered 19/3, 2012 at 11:47 Comment(1)
yea I read a tiny bit about that. They are both by Josh Smith, I think this technique came first because he says in that article that it was just a solution he posted to a question, then decided to blog about it. I think element spy is the result of that.Amias

© 2022 - 2024 — McMap. All rights reserved.