WPF Binding a MenuItem in a CompositeCollection not working

自作多情 提交于 2019-12-12 13:26:05

问题


I'm having problems binding a command to a menuitem in a compositecollection. The MenuItem is part of ContextMenu which is defined in the UserControl.Resources.

The problem is that the binding of the New label is not working. When I place the MenuItem outside of the composite collection it will work. Any ideas?

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

回答1:


This is happening because of the fact that ContextMenu is not in the same visual tree as its containing parent, resulting in data binding issues. Since the ContextMenu is not in the same visual tree, ElementName, RelativeSouce (FindAncestor), etc bindings will not work.

You can get around this through

  1. In the code behind for the UserControl:

    NameScope.SetNameScope(DataGridRowContextMenu, NameScope.GetNameScope(this)); 
    
  2. using the PlacementTarget property like this -

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

Or use other solutions from -

ElementName Binding from MenuItem in ContextMenu

WPF: DataContext for ContextMenu




回答2:


I keep struggling around with this crazy ContextMenu and its MenuItems for a long time. But I found a solution to work with it creating a custom behaviour "BindingProxyBehaviour" with a dependancyproperty named "Data". This property holds an object for example your DataContext(maybe your ViewModel if you use MVVM pattern).

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

Just add the BindingProxy as a resource in you xaml file like this.

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

In my case I am using a CompositeCollection to mix up static and dynamic MenuItems. Therefore the second resource with key "BindingProxyViewModelDynamicDataList".

Voilà now you can easily access your data no matter where your ContextMenu is. My ContexMenu position in xaml tree is UserControl->Grid->DataGrid->DataGridTemplateColumn->CellTemplate->DataTemplate->TextBox.Template->Grid->TextBlock->controls:IconButton(simple customButton control derived from button) and here we are inside the 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>

I hope that I could help sombody with this short post.




回答3:


Use CommandTarget property or make a staticRessource of your datacontext like

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


来源:https://stackoverflow.com/questions/11626114/wpf-binding-a-menuitem-in-a-compositecollection-not-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!