Как связать ContextMenu с каждым элементом TreeView?
И у меня есть 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 ответ
Вы получаете "Элемент уже является потомком другого элемента". исключение, потому что все элементы в 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>