WPF绑定CompositeCollection中的MenuItem无法工作

3

我在组合集合中绑定菜单项的命令时遇到了问题。 MenuItemUserControl.Resources 中定义的 ContextMenu 的一部分。

问题在于 New 标签的绑定不起作用。当我将 MenuItem 放在组合集合之外时,它可以正常工作。有任何想法吗?

<UserControl.Resources>
    <ContextMenu x:Key="DataGridRowContextMenu">
        <MenuItem Header=" Set label"/>
            <MenuItem.ItemsSource>
                    <CompositeCollection>
                            <CollectionContainer Collection="{Binding Source={StaticResource labelsSelectSource}}" />
                    <MenuItem Header=" New label..." 
                          Command="{Binding DataContext.NewLabel,
                                RelativeSource={RelativeSource Mode=FindAncestor,
                                AncestorType={x:Type UserControl}}}"/>

                        </CompositeCollection>
                 </MenuItem.ItemsSource>
            </MenuItem>
<UserControl.Resources/>
3个回答

1
这是因为ContextMenu不在与其包含父级相同的可视树中,导致数据绑定问题。由于ContextMenu不在同一可视树中,ElementNameRelativeSouceFindAncestor)等绑定将无法工作。
您可以通过以下方式解决此问题:
  1. 在UserControl的代码后台中:

    NameScope.SetNameScope(DataGridRowContextMenu, NameScope.GetNameScope(this)); 
    
  2. 使用PlacementTarget属性,如下所示 -

    <ContextMenu 
        x:Key="DataGridRowContextMenu">   
        DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
        .
        .  
        .
        <MenuItem 
            Header=" 新标签..."     
            Command="{Binding DataContext.NewLabel}"/> 
    

或者使用其他解决方案 -

在ContextMenu中从MenuItem绑定ElementName

WPF:ContextMenu的DataContext


有没有关于如何在UserControl.Resources中使用DataGridRowContextMenu并与样式绑定的建议?这时,x:Key名称在代码后台不可用。 - ferdyh
@Ferdy 在这种情况下,您可以在代码中使用FrameworkElement.FindResource方法,然后使用SetNamescope;还可以查看我的答案这里以获取相关信息。 - akjoshi

1

我已经在ContextMenu和其MenuItems上苦苦挣扎很久了。但是我找到了一种解决方案,通过创建一个名为"BindingProxyBehaviour"的自定义行为并添加一个名为"Data"的依赖属性来使用它。这个属性持有一个对象,比如你的DataContext(如果你使用MVVM模式可能是你的ViewModel)。

public class BindingProxyDataBehavior : Freezable
{
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxyDataBehavior), new UIPropertyMetadata(null));

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxyDataBehavior();
    }

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }
}

只需在您的xaml文件中将BindingProxy添加为资源,如下所示。

<UserControl.Resources>
    <ResourceDictionary>
        <behaviors:BindingProxyDataBehavior x:Key="BindingProxyViewModel" Data="{Binding}"/>
        <behaviors:BindingProxyDataBehavior x:Key="BindingProxyViewModelDynamicDataList" Data="{Binding DynamicDataListObject}"/>
    </ResourceDictionary>
</UserControl.Resources>

在我的情况下,我正在使用CompositeCollection来混合静态和动态的菜单项。因此第二个关键字为"BindingProxyViewModelDynamicDataList"的资源。

现在,您可以轻松访问数据,无论您的ContextMenu位于何处。 我的ContexMenu在xaml树中的位置是UserControl->Grid->DataGrid->DataGridTemplateColumn->CellTemplate->DataTemplate->TextBox.Template->Grid->TextBlock->controls:IconButton(简单的自定义按钮控件派生自button),现在我们在IconButton内部:

<controls:IconButton.ContextMenu>
<ContextMenu x:Name="AnyContextMenuName">
    <ContextMenu.Resources>
        <HierarchicalDataTemplate DataType="{x:Type DynamicDataListItemType}">
            <TextBlock Text="{Binding DynamicDataListItemProperty}"/>
        </HierarchicalDataTemplate>
    </ContextMenu.Resources>
    <ContextMenu.ItemsSource>
        <CompositeCollection>
            <CollectionContainer Collection="{Binding Source={StaticResource BindingProxyViewModelDynamicDataList}, Path=Data}"/>
            <Separator/>
            <MenuItem Header="Your static header" Command="{Binding Source={StaticResource BindingProxyViewModel}, Path=Data.ViewModelCommandForYourStaticMenuItem}"/>
        </CompositeCollection>
    </ContextMenu.ItemsSource>
    <ContextMenu.ItemContainerStyle>
        <Style>
            <Setter Property="MenuItem.Foreground" Value="{DynamicResource DefaultForegroundBrush}"/>
            <Setter Property="MenuItem.Command" Value="{Binding Source={StaticResource BindingProxyViewModel}, Path=Data.ViewModelCommandForDynamicMenuItems}"/>
            <Setter Property="MenuItem.CommandParameter" Value="{Binding}"/>
        </Style>
    </ContextMenu.ItemContainerStyle>
</ContextMenu>
</controls:IconButton.ContextMenu>

我希望这篇简短的文章能够帮助到一些人。


0

使用CommandTarget属性或将您的数据上下文制作为静态资源,例如

<MenuItem Header=" New label..." 
          Command="{Binding Path=NewLabel,Source={StaticResource viewModel}}"/>

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