Question

I'm trying to bind to a property of MainWindow, but from a ContextMenu within a DataTemplate. How can I achieve this?

  • I can't use ElementName, as the contextMenu isn't part of the visual tree
  • I can't use PlacementTarget, as this will give the UIElement produced by the DataTemplate

    <Window x:Class="WpfApplication24.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ItemsControl ItemsSource="{Binding Data}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border Padding="5" CornerRadius="10" BorderThickness="1" BorderBrush="Red">
                    <Border.ContextMenu>
                        <ContextMenu ItemsSource="{Binding <I want to bind to a property of MainWindow here>}"/>
                    </Border.ContextMenu>
                    <TextBlock Text="{Binding}"/>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

Was it helpful?

Solution

You can have the window object in Tag of your Border and then can access it using PlacementTarget.Tag

<DataTemplate>
   <Border Padding="5" CornerRadius="10" BorderThickness="1" BorderBrush="Red"
           Tag="{Binding RelativeSource={RelativeSource FindAncestor, 
                                                        AncestorType=Window}}">
       <Border.ContextMenu>
            <ContextMenu ItemsSource="{Binding PlacementTarget.Tag.PropertyName,
                                       RelativeSource={RelativeSource Self}}"/>
       </Border.ContextMenu>
       <TextBlock Text="{Binding}"/>
    </Border>
</DataTemplate>

OTHER TIPS

What I used is a simple custom control wrapper e.g. MyContextMenu
...with just one line of code, something like...

public class MyContextMenu : ContextMenu
{
    public override void EndInit()
    {
        base.EndInit();
        NameScope.SetNameScope(this, NameScope.GetNameScope(App.Current.MainWindow));
    }
}

...and use that instead of ContextMenu.

That always 'scopes' to the MainWindow which may not always be optimal - but you can use ElementName etc.

2) The other option is using NameScope.NameScope="{StaticResource myNameScope}"
NameScope.NameScope seems like an optimal solution - however, you cannot bind from it (and it binds 'too late').
But you can use {StaticResource ...} - and you make a class which wraps around MainWindow's scope.
Similar, but I found the above 'less disruptive' (you can pretty much write the code you'd normally write).


For more details take a look at this answers (and for more ideas)...

ElementName Binding from MenuItem in ContextMenu
How to access a control from a ContextMenu menuitem via the visual tree?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top