How to access a control from a ContextMenu menuitem via the visual tree?
Asked Answered
D

1

8

This seems to be a pretty popular topic, but...

I have the following XAML:

<internal:MyCommandObject x:Name="CommandModel"/>

<Button DockPanel.Dock="Bottom" Command="{Binding DoAction, ElementName=CommandModel}">
    <Button.ContextMenu>
        <ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
            <MenuItem Command="{Binding DoAction, ElementName=CommandModel}"/>
        </ContextMenu>
    </Button.ContextMenu>
    Click Me
</Button>

Now, MyCommandObject is a control which exposes dynamic commands from its DataContext. You know what's coming next. :)

Basically, the button command works perfectly - when I click it, the DoAction command on the MyCommandObject gets executed perfectly. However, the command in the menuitem doesn't get executed. I've tried various tricks such as setting the context menu datacontext to be the placementTarget so it can traverse the visual tree of the controls and so on, but nothing's doing.

What particular alignment of RelativeSource and CommandTarget runes do I need to get this to work?

Duggan answered 4/7, 2012 at 16:56 Comment(0)
B
3

This is happening because DataContext="{Binding PlacementTarget,... binding would set the button as MenuItems DataContext but that won't add the ContextMenu to the VisualTree of your window and that's why ElementName binding's won't work. A simple workaround to use ElementName bindings is to add this in your Window/UserControl's code-behind:

NameScope.SetNameScope(contextMenuName, NameScope.GetNameScope(this)); 

Another solution is to do this -

<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">   
    <MenuItem Command="{Binding DataContext.DoAction}"/>   
</ContextMenu>

DataContext="{Binding PlacementTarget,... will set the Button(Placementtarget) as the DataContext of your ContextMenu, so you can use Button's DataContext to bind command.

Update:

You can try and use the NameScope.NameScope Attached Property to set NameScope in XAML but I am not sure how you will get the NameScope of parent window without code!

You will have to do something similar to following article by Josh Smith, he provides a way to do this in XAML; but that too involves code (more then that single line of code) -

Enable ElementName Bindings with ElementSpy

Any specific reason to not use this single line of code?

Beaune answered 5/7, 2012 at 6:45 Comment(6)
Thanks for this. I'm trying not to use code-behind if possible - is it possible to define the NameScope in XAML? Unfortunately, I can't set the data context of the menu item to be the button that exposes the command. This is a context menu for a grid, and I need the DataContext of the selected item so that I can pass in properties from that as parameters to the command.Duggan
Reason for not using code behind is that the xaml isn't compiled - everything is done in the client at runtime using late binding.Duggan
@Duggan Not sure whether you are talking about just your scenario or in genral, but mostly XAML files are compiled into BAML at the time of build(looking at your xaml that looks the case too); I would suggest you to refer these links - Compiled XAML = BAML not IL and B is for... BAMLBeaune
Thanks akjoshi, I'm aware that a lot of people used compiled XAML. I'm not talking about the general case. It's a perfectly valid use-case to store XAML as a resource and parse/bind it at runtime (and, in fact, that's exactly how WPF/XAML used to be used in .Net 2.0 - XAML compilation only arrived in .Net 3.5/VS2008. I'm referring to my specific use-case which is an application that uses non-compiled XAML that's rendered dynamically at run-time based on various (server-based) configurations, and therefore can't be compiled.Duggan
Makes sense; I haven't used Loose XAML but as per this post A loose XAML file can't include the x:Class XAML keyword; so, if that is correct then you won't be able to achieve what you want!Beaune
Weird. I don't think that was me!Duggan

© 2022 - 2024 — McMap. All rights reserved.