【问题标题】:WPF Binding a MenuItem in a CompositeCollection not workingWPF在CompositeCollection中绑定MenuItem不起作用
【发布时间】:2012-07-24 07:31:16
【问题描述】:

我在将命令绑定到复合集合中的菜单项时遇到问题。 MenuItem 是在UserControl.Resources 中定义的ContextMenu 的一部分。

问题是新标签的绑定不起作用。当我将 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/>

【问题讨论】:

    标签: wpf xaml binding compositecollection


    【解决方案1】:

    发生这种情况是因为ContextMenu 与其包含的父级不在同一个visual tree 中,从而导致数据绑定问题。由于ContextMenu 不在同一个可视化树中,ElementName、RelativeSouce (FindAncestor) 等绑定将不起作用。

    你可以通过

    1. 在 UserControl 后面的代码中:

      NameScope.SetNameScope(DataGridRowContextMenu, NameScope.GetNameScope(this)); 
      
    2. 像这样使用PlacementTarget 属性 -

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

    或使用来自 -

    的其他解决方案

    ElementName Binding from MenuItem in ContextMenu

    WPF: DataContext for ContextMenu

    【讨论】:

    • 当 DataGridRowContextMenu 位于 UserControl.Resources 中并与样式绑定时,有什么建议吗?然后 x:Key 名称在后面的代码中不可用..
    • @Ferdy 在这种情况下你可以在代码中使用FrameworkElement.FindResource 方法,然后使用SetNamescope;另请查看我的回答 here 以获取相关信息。
    【解决方案2】:

    长期以来,我一直在为这个疯狂的 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); }
        }
    }
    

    只需像这样将 BindingProxy 作为资源添加到您的 xaml 文件中。

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

    在我的例子中,我使用 CompositeCollection 来混合静态和动态菜单项。因此第二个资源的键为“BindingProxyViewModelDynamicDataList”。

    现在,无论您的 ContextMenu 位于何处,您都可以轻松访问您的数据。 我在 xaml 树中的 ContexMenu 位置是 UserControl->Grid->DataGrid->DataGridTemplateColumn->CellTemplate->DataTemplate->TextBox.Template->Grid->TextBlock->controls:IconButton(simple customButton control derived from 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>
    

    我希望我可以通过这篇短文帮助一些人。

    【讨论】:

      【解决方案3】:

      使用CommandTarget 属性或为您的数据上下文创建一个静态资源源,例如

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

      【讨论】:

        猜你喜欢
        • 2020-10-28
        • 1970-01-01
        • 2011-10-02
        • 1970-01-01
        • 2015-04-12
        • 2018-07-15
        • 1970-01-01
        相关资源
        最近更新 更多