WPF ContextMenu问题: 我该如何设置ContextMenu的DataContext?

37

我遇到了一些麻烦,不知道如何在ContextMenu上正确设置DataContext

我有一个视图模型的集合,是一个ItemsControl的源。每个视图模型都有一个项目集合,这也是另一个ItemsControl的源。每个项目都用于绘制具有ContextMenu的图像。该ContextMenu中的MenuItems需要绑定到视图模型上的命令,但是ContextMenuPlacementTarget指向各个项目。

我的Xaml大致如下:

<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>

我该如何将 ContextMenuDataContext 设置为对应项目的父视图模型?
4个回答

54

ContextMenu在可视树之外。以下是应该获取数据上下文的xaml:

<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>

这篇post解释了这是如何工作的。


1
问题在于我不想绑定到PlacementTarget。我想要绑定到外部控件的DataContext。 - Ashley Grenon
你确定它们没有相同的DataContext(即outerControl和inner itemsControl)吗? - kevindaub
2
是的,使用 PlacementTarget 获取的 DataContext 太深了。我得到了一个项,但我需要的是包含该项的集合的视图模型。如果我能绑定到外部控件的 DataContext,那就太完美了。 - Ashley Grenon
我猜我需要内部ItemsControl的DataContext,但那是外部控件到图像的DataContext。 - Ashley Grenon
1
小心!不要像我一样在ContextMenu的DataContext值上放置Mode=OneTime。这会导致绑定失败,原因未知。 - NathanAldenSr
显示剩余3条评论

17
您可以使用标记扩展:
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;
    }
}

它让你可以做:

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

不错的想法,但也许这只适用于 ItemsControl 中的菜单?例如,在 ContentPresenter 中我无法使其工作。 - g t

2

我不喜欢使用标签,我更喜欢使用附加属性。

您需要添加附加属性:

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);
}

在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>

0

这段代码允许您同时使用全局和本地DataContext(整个用户控件和当前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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接