0

And I have a UserControl with a TreeView, and with a 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 ) {
    }

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

The usage:

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

First work everything fine, but after the second I obviously get the "Element is already the child of another element." exception.

Is it possible to solve that just with binding, without any code-behind?

1 Answer 1

1

You're getting the "Element is already the child of another element." exception because all the items in your TreeView has their ContextMenus bound the the same object (the ContextMenu you defined in ).

Instead of exposing the ContextMenu as a property in MyControl you can expose its HeirarchicalDataTemplate instead:

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

If you choose to go this way you will have to define the TreeView ItemTemplate outside of your original user control. In the outer client using the UserControl you can do this:

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

Doing it like this also increases the flexibility of your UserControl as a side effect because you can now customize the TreeView's ItemTemplate in the UserControl from the outside. You can put the HierarchicalDataTemplate in a ResourceDictionary if you want to consistently reuse it.


A second solution, if you're willing to use the code-behind, is to just use one ContextMenu for the entire UserControl and then programatically determine which item was selected in the code behind for the client.

    <my:MyControl>
        <my:MyControl.TreeViewItemTemplate>
            <controlsInputToolkit:ContextMenuService.ContextMenu>
                <!-- ContextMenu -->
            </controlsInputToolkit:ContextMenuService.ContextMenu>
        </my:MyControl.TreeViewItemTemplate>
    </my:MyControl>
Sign up to request clarification or add additional context in comments.

2 Comments

It works, but I can't bind commands to the MenuItems: <controlsInputToolkit:MenuItem Header="Properties" Command="{Binding ElementName=MyMainPage, Path=ShowPropertiesWindowCommand}" CommandParameter="{Binding Id}"> - doesn't work. If I change the ElementName, I don't get any binding exception. Any idea?
What exception is it giving you? Are you defining this binding in MyMainPage or a ResourceDictionary?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.