Как связать ContextMenu с каждым элементом TreeView? - PullRequest
0 голосов
/ 29 июля 2011

И у меня есть UserControl с TreeView и с ContextMenu DependencyProperty:

    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>

Сначала все работает нормально, но после второго я, очевидно, получаю «Элемент уже является потомком другого элемента».исключение.

Можно ли решить это просто с помощью привязки, без какого-либо кода позади?

1 Ответ

1 голос
/ 30 июля 2011

Вы получаете «Элемент уже является потомком другого элемента».исключение, потому что все элементы в 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 как побочного эффекта, поскольку теперь вы можете настроить ItemTemplate TreeView в UserControl извне,Вы можете поместить HierarchicalDataTemplate в ResourceDictionary, если хотите последовательно использовать его.


Второе решение, если вы хотите использовать выделенный код, это просто использовать одно ContextMenu для всегоUserControl, а затем программно определить, какой элемент был выбран в коде для клиента.

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