Binding ContextMenu to parent item's datacontext

Ribbon for WPF Forum

Posted 1 year ago by Farris
Version: 22.1.4
Avatar

Hello,

I am having trouble getting my ContextMenu to work, and I've tried all kinds of "hacks" without getting this to work.

The scenario is that I in the Ribbon menu have a ribbon Group, which has a ribbon Menu inside it which is bound to a list of viewmodels.

The menu has a DataTemplate which contains a button for each item, and each button has a ContextMenu, so that you can edit or delete the individual items. 

The problem is getting the bindings set correctly. This seems to have worked at some point before, but to solve another issue which made the ContextMenu behave incorrectly (the wrong ContextMenu was shown. "Minimize the ribbon" was shown instead of the Button's ContextMenu), I tried upgrading from version 19.1 to version 22.1. After the upgrade the correct ContextMenu was shown, but the command got passed a CheckableCommandParameter instead of the "BookmarkItemViewModel".

This is basically what it looks like, with some suggested fixes being left behind:

<ribbon:Ribbon>
    <ribbon:Tab Label="Home">
        <ribbon:Group Label="Bookmarks">
            <ribbon:Menu ItemsSource="{Binding Bookmarks.BookmarkItems}">
                <ribbon:Menu.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:BookmarkItemViewModel}">
                        <ribbon:Button Label="{Binding Path=Title}" Command="{Binding OpenUrlCommand}" CommandParameter="{Binding Url}" Tag="{Binding}">
                            <ribbon:Button.ContextMenu>
                                <ribbon:ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                                    <ribbon:Button Context="MenuItem" Label="Edit" Command="{Binding EditCommand}" CommandParameter="{Binding}"/>
                                    <ribbon:Button Context="MenuItem" Label="Delete" Command="{Binding DeleteCommand}" CommandParameter="{Binding}" />
                                </ribbon:ContextMenu>
                            </ribbon:Button.ContextMenu>
                        </ribbon:Button>
                    </DataTemplate>
                </ribbon:Menu.ItemTemplate>
            </ribbon:Menu>
        </ribbon:Group>
    </ribbon:Tab>
</ribbon:Ribbon>

The ideal solution would be to bind the commands to the viewmodel which holds all the items (which the ribbon Menu is bound to), and pass the item into the command as a commandparameter. 

Do you know of any simple way to bind the ContextMenu to its parent's DataContext? It doesn't have to be an exact example, but I have not been able to find anything that works. And It seems this at some point worked before, with the earlier version of Actipro controls, though I haven't been able to verify that.

[Modified 1 year ago]

Comments (4)

Posted 1 year ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

ribbon:Menu is really intended to be used within a menu context to give its items a menu appearance, and wasn't really built for use in a ribbon:Group.  Would something like a basic ItemsControl be better here?  

Regardless though, context menus are tough since they are outside the visual tree of their target.  I would think that using PlacementTarget from the ContextMenu would get you the ribbon:Button instance in your DataTemplate.  And the DataContext of that would be the BookmarkItemViewModel.  You shouldn't need to also put use the Tag="{Binding}" since that value should already be in DataContext.  I would think doing this would work for the ribbon:ContextMenu:

DataContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"


Actipro Software Support

Posted 1 year ago by Farris
Avatar

Yeah, it being in its own visual tree is what makes this difficult. Adding to that, it seems that since this all is in a DataTemplate, it complicates things further, since other examples where it is not in a DataTemplate have got this working (standard ContextMenu examples, not Actipro).

If I set the DataContext as you suggest, it behaves the same as with using the Tag. Since this is a bit tricky to inspect with the Live Visual Tree in Visual Studio (the ContextMenu closes when Visual Studio gets focus), I tried using Snoop: Screenshot here

It only points out that there's an error with the binding, "Get error messages" does nothing. Thinking I need to approach this differently, but it is strange if this approach has worked with an earlier version of Actipro ribbon control.

It also seems like this is something that should be simple to achieve, but like you say, the ContextMenu existing on its own visual tree complicates this.

Answer - Posted 1 year ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Farris,

I did some tests and also had trouble getting to the data context properly.  But I eventually was able to rework the XAML to get the lowest level ribbon:Button to show the desired BookmarkItemViewModel in its label like this:

<ribbon:Ribbon>
    <ribbon:Tab Label="Home">
        <ribbon:Group Label="Bookmarks">
            <ribbon:Menu ItemsSource="{Binding ElementName=window, Path=Bookmarks}">
                <ribbon:Menu.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:BookmarkItemViewModel}">
                        <ribbon:Button x:Name="button" Label="{Binding Path=Label}">
                            <ribbon:Button.ContextMenu>
                                <ribbon:ContextMenu DataContext="{x:Reference button}">
                                    <ribbon:Menu>
                                        <ribbon:Button Label="{Binding DataContext}" />
                                    </ribbon:Menu>
                                </ribbon:ContextMenu>
                            </ribbon:Button.ContextMenu>
                        </ribbon:Button>
                    </DataTemplate>
                </ribbon:Menu.ItemTemplate>
            </ribbon:Menu>
        </ribbon:Group>
    </ribbon:Tab>
</ribbon:Ribbon>

You'd want to change the Label binding to something off that VM class and add the appropriate commands, etc.  But it seems to get the button reference into the context menu ok, to the point you can get the DataContext from it.

This Ribbon product had some extra complexity in terms of how it implemented menu items, trying to make other controls like ribbon:Button change templates to work within them, with a ribbon:Menu required to get the functionality correct.  That complexity can make some things like this data binding more difficult to achieve.  In the upcoming 23.1 version, we will be releasing a new Bars product that has a new Ribbon control implementation as well as other toolbar/menu controls.  One of the goals of the new implementation is to use a better design where all controls inherit an appropriate native control (our bars:BarMenuItem will inherit MenuItem instead of rigging something like ribbon:Button to work in a menu).  And one of the largest improvements over the current Ribbon is that the entire Bars version of Ribbon has been designed to support MVVM from the ground up.  Keep an eye on our site as we will have more information on the new Bars product very soon.


Actipro Software Support

Posted 1 year ago by Farris
Avatar

Thank you, this helped me get a working solution. 

Will also keep a look-out for the next version!

The latest build of this product (v24.1.1) was released 2 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.