我有一个带有TreeView的UserControl,以及一个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>
首先做的一切都很好,但是在第二次之后我明显得到了“元素已经是另一个元素的孩子”。异常。
是否可以通过绑定解决这个问题,而不需要任何代码隐藏?
答案 0 :(得分: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>