dkalkwarf
dkalkwarf

Reputation: 313

Correct binding for ContextMenu in custom control

I cannot find the correct binding for a ContextMenu menu item in my custom control.

<ContextMenu x:Key="MyContextMenu">
    <MenuItem Header="MyMenuItem"
              Command="{Binding PlacementTarget.MyCommand, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>

<Style TargetType="{x:Type local:MyControl}" x:Shared="False">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyControl}">
                <DockPanel ContextMenu="{StaticResource MyContextMenu}">
                    <!--some controls-->
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

MyCommand is defined in MyControl.xaml.cs and is the command I wish to bind to the menu item.

The binding in the example looks for MyCommand in DockPanel. What is the correct binding?

Upvotes: 2

Views: 2484

Answers (3)

Muds
Muds

Reputation: 4116

ContextMenu is not contained in VisualTree. hence no datacontext.. do this to have it working ..

<MenuItem Command="{Binding Path=PlacementTarget.DataContext.MyCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />

Upvotes: 0

dkalkwarf
dkalkwarf

Reputation: 313

Mr. Nimrod's answer didn't quite solve my problem, but provided a technique for doing so. Below is what worked.

The problem with ContextMenu is that you cannot use RelativeSource to traverse the element tree to an arbitrary control: you can only go as far as its container (which you refer to with PlacementTarget).

The basic strategy is to assign Tag in the ContextMenu's container. Tag refers to whatever control, command, etc. you are interested in. In my case I assigned it to TemplatedParent, which allowed me to bind to the MenuItem.Command to MyCommand.

<Style TargetType="{x:Type local:MyControl}" x:Shared="False">
    <Style.Resources>
        <ResourceDictionary>
            <ContextMenu x:Key="MyContextMenu">
                <MenuItem Header="MyItem"
                          Command="{Binding Parent.PlacementTarget.Tag.MyCommand, RelativeSource={RelativeSource Self}}"/>
            </ContextMenu>
        </ResourceDictionary>
    </Style.Resources>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyControl}">                
                <DockPanel ContextMenu="{StaticResource MyContextMenu}" Tag="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                    <!--some controls-->                
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Upvotes: 0

Scott Nimrod
Scott Nimrod

Reputation: 11570

You need to add a tag to the menu's container and bind to it using placement target.

View this example:

<StackPanel x:Key="ConfigurationListItem" x:Shared="False" Tag="{Binding ElementName=UserControl}">
        <StackPanel.ContextMenu>
            <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" Tag="{Binding}">
                <MenuItem Header="Sync Environment Dependencies" 
                        Command="{Binding Parent.PlacementTarget.Tag.SyncEnvironmentCommand, RelativeSource={RelativeSource Self}}"
                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext}" />
            </ContextMenu>
        </StackPanel.ContextMenu>
    </StackPanel>

Upvotes: 2

Related Questions