【问题标题】:How to bind a ContextMenu to each TreeView item?如何将 ContextMenu 绑定到每个 TreeView 项?
【发布时间】:2011-10-16 00:13:36
【问题描述】:

我有一个带有 TreeView 和 ContextMenu DependencyProperty 的 UserControl:

    public ObservableCollection<Control> ContextMenu {
        get {
            return ( ObservableCollection<Control> )GetValue( ContextMenuProperty );
        }
        set {
            SetValue( ContextMenuProperty, value );
        }
    }

    public static readonly DependencyProperty ContextMenuProperty =
        DependencyProperty.Register( "ContextMenu", typeof( ObservableCollection<Control> ), typeof( FilterableTreeViewControl ),
        new PropertyMetadata( new ObservableCollection<Control>(), new PropertyChangedCallback( FilterableTreeViewControl.OnContextMenuPropertyChange ) ) );

    private static void OnContextMenuPropertyChange( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
        FilterableTreeViewControl ctrl = d as FilterableTreeViewControl;
        ctrl.OnContextMenuChange( ( Object )e.NewValue );
    }

    protected virtual void OnContextMenuChange( Object NewItemsSource ) {
    }

XAML:

        <controlsToolkit:TreeViewDragDropTarget AllowDrop="True" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Drop="TreeViewDragDropTarget_Drop" AllowedSourceEffects="All">
            <controlsToolkit:TreeViewDragDropTarget.Resources>
                <Data:HierarchicalDataTemplate x:Key="TreeViewTemplate" ItemsSource="{Binding Children}">
                    <StackPanel Orientation="Horizontal" Height="Auto" Width="Auto">
                        <Image Source="{Binding Type,Converter={StaticResource TreeIconConverter}}" />
                        <TextBlock x:Name="NameTextBlock" Text="{Binding Name}">
                            <controlsInputToolkit:ContextMenuService.ContextMenu>
                                <controlsInputToolkit:ContextMenu ItemsSource="{Binding ElementName=MyTreeViewControl, Path=ContextMenu}" />
                            </controlsInputToolkit:ContextMenuService.ContextMenu>
                        </TextBlock>
                    </StackPanel>
                </Data:HierarchicalDataTemplate>
            </controlsToolkit:TreeViewDragDropTarget.Resources>
            <Controls:TreeView Name="treeView" ItemTemplate="{StaticResource TreeViewTemplate}">
            </Controls:TreeView>
        </controlsToolkit:TreeViewDragDropTarget>

用法:

        <my:MyControl
                DragEnabled="False"
                ItemsSource="{Binding TreeRootNodes}" 
                FilterCaption="Filter:" 
                SelectionChangedCommand="{Binding SelectedMachineGroupChangedCommand_L}"
                DropCommand="{Binding DropCommand}">
            <my:FilterableTreeViewControl.ContextMenu>
                <controlsInputToolkit:MenuItem Header="Menu 1" />
                <controlsInputToolkit:MenuItem Header="Menu 2" />
                <controlsInputToolkit:MenuItem Header="Menu 3" />
            </my:MyControl.ContextMenu>
        </my:MyControl>

首先一切正常,但第二次之后我显然得到“元素已经是另一个元素的子元素”。例外。

是否可以仅通过绑定来解决这个问题,而无需任何代码隐藏?

【问题讨论】:

    标签: .net silverlight binding custom-contextmenu


    【解决方案1】:

    您得到“元素已经是另一个元素的子元素”。例外,因为 TreeView 中的所有项目的 ContextMenus 都绑定了同一个对象(您在 中定义的 ContextMenu)。

    您可以公开其 HeirarchicalDataTemplate,而不是将 ContextMenu 作为属性公开在 MyControl 中:

    public HeirarchicalDataTemplate TreeViewItemTemplate {
        get {
            return (HeirarchicalDataTemplate)this.treeView.ItemTemplate; 
        }
        set {
            this.treeView.ItemTemplate = value;
        }
    }
    

    如果您选择采用这种方式,则必须在原始用户控件之外定义 TreeView ItemTemplate。在使用 UserControl 的外部客户端中,您可以这样做:

        <my:MyControl>
            <my:MyControl.TreeViewItemTemplate>
                <Data:HierarchicalDataTemplate>
                       <!-- Rest of the template -->
                        <TextBlock x:Name="NameTextBlock" Text="{Binding Name}">
                            <controlsInputToolkit:ContextMenuService.ContextMenu>
                                <!-- ContextMenu -->
                            </controlsInputToolkit:ContextMenuService.ContextMenu>
                        <!-- Rest of the template -->
                </Data:HierarchicalDataTemplate>
            </my:MyControl.TreeViewItemTemplate>
        </my:MyControl>
    

    这样做也增加了 UserControl 作为副作用的灵活性,因为您现在可以从外部自定义 UserControl 中的 TreeView 的 ItemTemplate。如果您想始终如一地重用它,可以将 HierarchicalDataTemplate 放在 ResourceDictionary 中。


    如果您愿意使用代码隐藏,则第二种解决方案是只为整个 UserControl 使用一个 ContextMenu,然后以编程方式确定在客户端的代码中选择了哪个项目。

        <my:MyControl>
            <my:MyControl.TreeViewItemTemplate>
                <controlsInputToolkit:ContextMenuService.ContextMenu>
                    <!-- ContextMenu -->
                </controlsInputToolkit:ContextMenuService.ContextMenu>
            </my:MyControl.TreeViewItemTemplate>
        </my:MyControl>
    

    【讨论】:

    • 它可以工作,但我无法将命令绑定到菜单项:&lt;controlsInputToolkit:MenuItem Header="Properties" Command="{Binding ElementName=MyMainPage, Path=ShowPropertiesWindowCommand}" CommandParameter="{Binding Id}"&gt; - 不起作用。如果我更改 ElementName,我不会收到任何绑定异常。有什么想法吗?
    • 它给了你什么例外?您是在 MyMainPage 或 ResourceDictionary 中定义此绑定吗?
    猜你喜欢
    • 1970-01-01
    • 2015-07-12
    • 2014-08-06
    • 2012-01-26
    • 2020-03-10
    • 1970-01-01
    • 2013-05-25
    • 2010-12-07
    • 1970-01-01
    相关资源
    最近更新 更多