• 【wpf】 当用了数据模板之后如何获取控件的Item?


    背景

     我对一个treeview使用了数据模板

    1. <TreeView.ItemTemplate>
    2. <HierarchicalDataTemplate DataType="{x:Type local_md:ToolsNodeItem}" ItemsSource="{Binding Path=Children}">
    3. <StackPanel Orientation="Horizontal" Background="Transparent">
    4. <Image Source="{Binding Icon}" Width="20" Margin="0,0,10,0"/>
    5. <TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center"/>
    6. StackPanel>
    7. HierarchicalDataTemplate>
    8. TreeView.ItemTemplate>

    数据源的数据结构为:

    1. public class ToolsNodeItem
    2. {
    3. public string Icon { get; set; }
    4. public string DisplayName { get; set; }
    5. //Tree的绑定源就要这样套娃!
    6. public ObservableCollection Children { get; set; }
    7. public ToolsNodeItem()
    8. {
    9. Children = new ObservableCollection();
    10. }
    11. }

    完成后大概就是这个样子

     现在我的需求是,右键点击这些节点,弹出一个右键菜单然后对节点进行操作。比如删除这个节点。

    弹出菜单不难:(加上这段就行)

    1. <StackPanel.ContextMenu>
    2. <ContextMenu>
    3. <MenuItem Header="删除" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="删除"/>
    4. <MenuItem Header="上移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="上移"/>
    5. <MenuItem Header="下移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="下移"/>
    6. ContextMenu>
    7. StackPanel.ContextMenu>

    现在问题来了,因为是右键菜单,你会发现右键单击后,菜单是弹出来了,但是此时节点并没有被选中,所以此时即使你为treeview设置了PreviewMouseLeftButtonDown这些事件,然后查看sender(treeview)发现,treeview的SelectItem依然是null,这样就无法确认到底是那个item别选中,就没办法对item操作。所以首先要想办法让右键能选中控件。

    于是我需要将模板中的StackPanel添加鼠标右键事件:下面这种是MVVM的写法!

    1. <TreeView.ItemTemplate>
    2. <HierarchicalDataTemplate DataType="{x:Type local_md:ToolsNodeItem}" ItemsSource="{Binding Path=Children}">
    3. <StackPanel Orientation="Horizontal" Background="Transparent">
    4. <i:Interaction.Triggers>
    5. <i:EventTrigger EventName="PreviewMouseRightButtonDown">
    6. <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectTreeNodeCommand}" CommandParameter="{Binding}" />
    7. i:EventTrigger>
    8. i:Interaction.Triggers>
    9. <StackPanel.ContextMenu>
    10. <ContextMenu>
    11. <MenuItem Header="删除" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="删除"/>
    12. <MenuItem Header="上移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="上移"/>
    13. <MenuItem Header="下移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}" CommandParameter="下移"/>
    14. ContextMenu>
    15. StackPanel.ContextMenu>
    16. <Image Source="{Binding Icon}" Width="20" Margin="0,0,10,0"/>
    17. <TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center"/>
    18. <StackPanel.ToolTip>
    19. <TextBlock Text="{Binding TypeName}"/>
    20. StackPanel.ToolTip>
    21. StackPanel>
    22. HierarchicalDataTemplate>
    23. TreeView.ItemTemplate>

    这里需要说明一下:以前,我都是这么写的:

    1. <i:EventTrigger EventName="PreviewMouseRightButtonDown">
    2. <prism:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectTreeNodeCommand}"/>
    3. 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的哪个位置都一样)

    ItemContainerStyle

    回想到老师的这句话:

     我拿不到TreeViewItem但是我可以,绑定TreeViewItem的IsSelected属性啊!但是数据模板不是TreeViewItem啊?

    这就要看 ItemContainerStyle 这个样式了,它规定了子项的样式,而该样式就可以设置属性IsSelected!

    这个几个容易混淆的概念,大家可以对比一下:

    这些都是,wpf给我提供的接口,“入侵” 控件内部的“密码”,只有搞清楚这些,我们才能更好的实现数据驱动界面!将bingding用的更加的灵活。

    于是,我又在ToolsNodeItem模型中增加了一个属性IsSelected

    1. private bool isSelected =false;
    2. public bool IsSelected
    3. {
    4. get { return isSelected; }
    5. set { SetProperty(ref isSelected, value); }
    6. }

    前台代码添加部分:

     BasedOn="{StaticResource {x:Type TreeViewItem}}"

    这里的BasedOn是为了继续使用handcontrol的样式,覆盖默认的样式。

    数据驱动界面

    最后将ToolsNodeItem中的IsSelected设置为True,这样就能通过数据IsSelected,驱动界面TreeViewItem的IsSelected为True,从而选中。

    所以使用了数据模板之后,就要将 数据驱动界面 进行到底。

    结果展示

     实现了数据绑定之后,上下移动也是很简单的,移动数据源中的数据即可:

    1. private void WorkflowModify(string para)
    2. {
    3. var toolsNodeItem = workflowTreeViewObj.SelectedItem as ToolsNodeItem;
    4. if (toolsNodeItem != null)
    5. {
    6. var cur_index = WorkflowViewToolsTree.IndexOf(toolsNodeItem);
    7. if (para == "删除")
    8. {
    9. WorkflowViewToolsTree.Remove(toolsNodeItem);
    10. }
    11. else if (para == "上移")
    12. {
    13. cur_index = WorkflowViewToolsTree.IndexOf(toolsNodeItem);
    14. if (cur_index != 0)
    15. {
    16. WorkflowViewToolsTree.Move(cur_index, cur_index-1);
    17. }
    18. else
    19. {
    20. MessageBox.Show("到顶了");
    21. }
    22. }
    23. else if (para == "下移")
    24. {
    25. if (cur_index != WorkflowViewToolsTree.Count-1)
    26. {
    27. WorkflowViewToolsTree.Move(cur_index, cur_index + 1);
    28. }
    29. else
    30. {
    31. MessageBox.Show("到地板了");
    32. }
    33. }
    34. }
    35. }

    番外

    其实,用了模板后,有一种方法能拿到TreeViewItem

     不过也是,选中之后的事情了所以,不适合当前背景需求。

  • 相关阅读:
    基于Python Scrapy爬虫改进KNN算法的网站分类系统
    计算机毕业设计Java“传情旧物”网站(源码+系统+mysql数据库+lw文档)
    基于SSM的地方文创特产在线商城【数据库设计、源码、开题报告】
    介绍 TensorFlow 的基本概念和使用场景
    所见即所得的3D打印建模设计
    域名放Cloudflare託管後,email發不出去怎麼辦
    代码随想录-刷题第二天
    高济健康:数字化科技创新与新零售碰撞 助推医疗产业优化升级
    分割学习(loss and Evaluation)
    Java项目构建之统一结果返回,统一异常处理
  • 原文地址:https://blog.csdn.net/songhuangong123/article/details/128137739