0

我有一个带有 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>

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

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

4

1 回答 1

1

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

除了将 ContextMenu 公开为 MyControl 中的属性,您还可以公开其 HeirarchicalDataTemplate:

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>
于 2011-07-29T22:45:54.840 回答