我对一个treeview使用了数据模板
- <TreeView.ItemTemplate>
-
- <HierarchicalDataTemplate DataType="{x:Type local_md:ToolsNodeItem}" ItemsSource="{Binding Path=Children}">
- <StackPanel Orientation="Horizontal" Background="Transparent">
- <Image Source="{Binding Icon}" Width="20" Margin="0,0,10,0"/>
- <TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center"/>
- StackPanel>
- HierarchicalDataTemplate>
- TreeView.ItemTemplate>
数据源的数据结构为:
- public class ToolsNodeItem
- {
-
- public string Icon { get; set; }
- public string DisplayName { get; set; }
-
-
- //Tree的绑定源就要这样套娃!
- public ObservableCollection
Children { get; set; } -
- public ToolsNodeItem()
- {
- Children = new ObservableCollection
(); - }
-
- }
完成后大概就是这个样子
现在我的需求是,右键点击这些节点,弹出一个右键菜单然后对节点进行操作。比如删除这个节点。
弹出菜单不难:(加上这段就行)
- <StackPanel.ContextMenu>
- <ContextMenu>
- <MenuItem Header="删除" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="删除"/>
- <MenuItem Header="上移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="上移"/>
- <MenuItem Header="下移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="下移"/>
- ContextMenu>
- StackPanel.ContextMenu>
现在问题来了,因为是右键菜单,你会发现右键单击后,菜单是弹出来了,但是此时节点并没有被选中,所以此时即使你为treeview设置了PreviewMouseLeftButtonDown这些事件,然后查看sender(treeview)发现,treeview的SelectItem依然是null,这样就无法确认到底是那个item别选中,就没办法对item操作。所以首先要想办法让右键能选中控件。
于是我需要将模板中的StackPanel添加鼠标右键事件:下面这种是MVVM的写法!
- <TreeView.ItemTemplate>
-
- <HierarchicalDataTemplate DataType="{x:Type local_md:ToolsNodeItem}" ItemsSource="{Binding Path=Children}">
- <StackPanel Orientation="Horizontal" Background="Transparent">
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="PreviewMouseRightButtonDown">
- <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectTreeNodeCommand}" CommandParameter="{Binding}" />
- i:EventTrigger>
- i:Interaction.Triggers>
- <StackPanel.ContextMenu>
- <ContextMenu>
- <MenuItem Header="删除" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="删除"/>
- <MenuItem Header="上移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="上移"/>
- <MenuItem Header="下移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="下移"/>
- ContextMenu>
- StackPanel.ContextMenu>
- <Image Source="{Binding Icon}" Width="20" Margin="0,0,10,0"/>
- <TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center"/>
- <StackPanel.ToolTip>
- <TextBlock Text="{Binding TypeName}"/>
- StackPanel.ToolTip>
- StackPanel>
- HierarchicalDataTemplate>
- TreeView.ItemTemplate>
这里需要说明一下:以前,我都是这么写的:
- <i:EventTrigger EventName="PreviewMouseRightButtonDown">
- <prism:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectTreeNodeCommand}"/>
- i:EventTrigger>
这样写,命令接收的参数就是我们平时使用事件的e的内容,但是由于我们用到了数据模板,导致e中的内容是和你点击的地方不同而不同的:
如果你点击的位置是TreeViewItem的图片位置,那么这里的OriginalSource以及Source就是Image,如果你点到的是文字位置对应的就是TextBlock。如果不使用数据模板的话,我们得到的其实就是TreeViewItem,然后使用TreeViewItem对象,调用Forks就好了。
用了数据版本就没法拿到TreeViewItem吗?
于是我咨询了一下老师:
目前我认为,用了数据版本就基本没办法拿到TreeViewItem了。
于是就有了:
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectTreeNodeCommand}" CommandParameter="{Binding}" />
有了它,也拿不到TreeViewItem,但是得到了ToolsNodeItem,就是TreeViewItem对应的数据源的子项:(不管你点击TreeViewItem的哪个位置都一样)
回想到老师的这句话:
我拿不到TreeViewItem但是我可以,绑定TreeViewItem的IsSelected属性啊!但是数据模板不是TreeViewItem啊?
这就要看 ItemContainerStyle 这个样式了,它规定了子项的样式,而该样式就可以设置属性IsSelected!
这个几个容易混淆的概念,大家可以对比一下:
这些都是,wpf给我提供的接口,“入侵” 控件内部的“密码”,只有搞清楚这些,我们才能更好的实现数据驱动界面!将bingding用的更加的灵活。
于是,我又在ToolsNodeItem模型中增加了一个属性IsSelected
- private bool isSelected =false;
-
- public bool IsSelected
- {
- get { return isSelected; }
- set { SetProperty(ref isSelected, value); }
- }
前台代码添加部分:
BasedOn="{StaticResource {x:Type TreeViewItem}}"
这里的BasedOn是为了继续使用handcontrol的样式,覆盖默认的样式。
最后将ToolsNodeItem中的IsSelected设置为True,这样就能通过数据IsSelected,驱动界面TreeViewItem的IsSelected为True,从而选中。
所以使用了数据模板之后,就要将 数据驱动界面 进行到底。
实现了数据绑定之后,上下移动也是很简单的,移动数据源中的数据即可:
- private void WorkflowModify(string para)
- {
- var toolsNodeItem = workflowTreeViewObj.SelectedItem as ToolsNodeItem;
- if (toolsNodeItem != null)
- {
- var cur_index = WorkflowViewToolsTree.IndexOf(toolsNodeItem);
- if (para == "删除")
- {
- WorkflowViewToolsTree.Remove(toolsNodeItem);
- }
- else if (para == "上移")
- {
- cur_index = WorkflowViewToolsTree.IndexOf(toolsNodeItem);
- if (cur_index != 0)
- {
- WorkflowViewToolsTree.Move(cur_index, cur_index-1);
- }
- else
- {
- MessageBox.Show("到顶了");
- }
-
- }
- else if (para == "下移")
- {
- if (cur_index != WorkflowViewToolsTree.Count-1)
- {
- WorkflowViewToolsTree.Move(cur_index, cur_index + 1);
- }
- else
- {
- MessageBox.Show("到地板了");
- }
- }
- }
- }
其实,用了模板后,有一种方法能拿到TreeViewItem
不过也是,选中之后的事情了所以,不适合当前背景需求。