38

I am having some trouble figuring out how to set the correct DataContext on a ContextMenu.

I have a collection of view models who are the source of an ItemsControl. Each view model has a collection of items which are also the source of another ItemsControl. Each item is used to draw image which has a ContextMenu. The MenuItems in that ContextMenu need to bind to a command on the view model, but the PlacementTarget of the ContextMenu is pointing to the individual item.

My Xaml looks something like this:

<ItemsControl ItemsSource="{Binding Markers"}> <ItemsControl.ItemTemplate> <DataTemplate> <ItemsControl ItemsSource="{Binding Items}"> <ItemsControl.ItemTemplate> <DataTemplate> <Image> <Image.ContextMenu> <ContextMenu> <MenuItem Header="Edit" Command="{Binding EditCommand}" /> </ContextMenu> </Image.ContextMenu> </Image> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> 

How can I set the DataContext of the ContextMenu to the item's corresponding parent view model?

4 Answers 4

56

The ContextMenu is outside of the visual tree. Below is the xaml that should get you the datacontext:

<ItemsControl ItemsSource="{Binding Markers}" Tag="{Binding ElementName=outerControl, Path=DataContext}"> ... <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}"> <MenuItem Header="Edit" Command="{Binding EditCommand}" /> </ContextMenu> ... </ItemsControl> 

This post explains how this works.

Sign up to request clarification or add additional context in comments.

8 Comments

The problem with this is I do not want to bind to what the PlacementTarget is. I want to bind to the DataContext of the outer control.
Are you sure they don't have the same DataContext (ie outerControl and inner itemsControl)?
Yes, the DataContext I get using PlacementTarget is a level too deep. I get back the an item, but what I need is the view model that has the collection that contains that item. If I could bind to the DataContext of the outer control, that would be perfect.
I guess it would be, I need the DataContext of the Inner ItemsControl, but that is the DataContext of the outer control to the Image.
Be careful! Don't put Mode=OneTimeon the ContextMenu's DataContext value as I did. This will cause the bindings to fail for some reason.
|
17

You can use a markupextension:

using System; using System.Windows.Controls; using System.Windows.Markup; using System.Xaml; [MarkupExtensionReturnType(typeof(ContentControl))] public class RootObject : MarkupExtension { public override object ProvideValue(IServiceProvider serviceProvider) { var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider)); return rootObjectProvider?.RootObject; } } 

It lets you do:

<ItemsControl ItemsSource="{Binding Markers}"> ... <ContextMenu DataContext="{Binding DataContext, Source={local:RootObject}}"> <MenuItem Header="Edit" Command="{Binding EditCommand}" /> </ContextMenu> ... </ItemsControl> 

2 Comments

Nice idea, but maybe this only works for menus within an ItemsControl? I could not get it working in a ContentPresenter for example.
This worked for me whereas lots didn't. Thanks. 👍
2

I don't like use Tag. I prefer attached property.

You need add attached property:

public static readonly DependencyProperty DataContextExProperty = DependencyProperty.RegisterAttached("DataContextEx", typeof(Object), typeof(DependencyObjectAttached)); public static Object GetDataContextEx(DependencyObject element) { return element.GetValue(DataContextExProperty); } public static void SetDataContextEx(DependencyObject element, Object value) { element.SetValue(DataContextExProperty, value); } 

In XAML:

<Button attached:DependencyObjectAttached.DataContextEx="{Binding ElementName=MyDataContextElement, Path=DataContext}"> <Button.ContextMenu> <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.(attached:DependencyObjectAttached.DataContextEx)}"> </ContextMenu> </Button.ContextMenu> </Button> 

Comments

0

This code allows you to use both the global and local DataContext (of entire userControl and of current TreeViewItem):

<TreeView Grid.Row="0" ItemsSource="{Binding Path=SelectableEnterprises}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}"> <Grid Height="20" Tag="{Binding DataContext, ElementName=userControl}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="25"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.ContextMenu> <ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}" > <ContextMenu.Visibility> <MultiBinding Converter="{converters:SurveyReportVisibilityConverter}"> <Binding Path="DataContext"/> <!--local DataContext--> <Binding Path="Tag.SelectedSurvey"/> <!--global DataContext--> </MultiBinding> </ContextMenu.Visibility> <MenuItem Header="Show HTML-report" Command ="{Binding Tag.OpenHTMLReportCommand}" CommandParameter="{Binding DataContext}" /> </ContextMenu> </Grid.ContextMenu> 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.