How to associate the dependency property of MenuItem.Header with Window / UserControl?

I am wondering how can I associate MenuItem.Header with the parent dependency property of Window / UserControl? Here is a simple example:

Window1.xaml

<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" x:Name="self"> <Grid> <Grid.ContextMenu> <ContextMenu> <MenuItem Header="{Binding Path=MenuText, ElementName=self}" /> </ContextMenu> </Grid.ContextMenu> <TextBlock Text="{Binding Path=MenuText, ElementName=self}"/> </Grid> </Window> 

Window1.xaml.cs

 public partial class Window1 : Window { public static readonly DependencyProperty MenuTextProperty = DependencyProperty.Register( "MenuText", typeof (string), typeof (Window1), new PropertyMetadata("Item 1")); public Window1() { InitializeComponent(); } public string MenuText { get { return (string)this.GetValue(MenuTextProperty); } set { this.SetValue(MenuTextProperty, value); } } } 

In my case, textblock displays "Item 1", and an empty item is displayed in the context menu. What am I doing wrong? It seems to me that I have come across a serious misunderstanding of WPF binding principles.

+4
source share
2 answers

You should see this in the Visual Studio output window:

Error System.Windows.Data: 4: Unable to find source for binding with reference 'ElementName = I.' BindingExpression: Path = MenuText; DataItem = NULL; target element 'MenuItem' (Name = ''); target property "Heading" (type "Object")

This is because ContextMenu is disconnected from VisualTree, you need to do this Binding differently.

One way is through ContextMenu.PlacementTarget (which should be a grid), you can use your DataContext to set the binding, for example:

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

or configure the DataContext in the ContextMenu itself:

 <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.DataContext}"> <MenuItem Header="{Binding Path=MenuText}"/> </ContextMenu> 

If this is not an option (since the DataContext Grid cannot be a Window / UserControl), you can try passing a link to the Window / UserControl via the Tag your grid, for example.

 <Grid ... Tag="{x:Reference self}"> <Grid.ContextMenu> <!-- The DataContext is now bound to PlacementTarget.Tag --> <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Tag}"> <MenuItem Header="{Binding Path=MenuText}"/> </ContextMenu> ... 

As a side note: because of this behavior, I tend to define a helper style in App.xaml to make all ContextMenus β€œinherit” the DataContext from their parent:

  <!-- Context Menu Helper --> <Style TargetType="{x:Type ContextMenu}"> <Setter Property="DataContext" Value="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"/> </Style> 
+7
source

An alternative to the HB solution is the attached behavior: Context MenuServiceExtensions.DataContext Attached Property

+1
source

All Articles