WPF—TreeView

作者:追风剑情 发布于:2020-12-23 17:44 分类:C#

xaml


  1. <Window x:Class="DataEditor.JsonWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:local="clr-namespace:DataEditor"
  7. mc:Ignorable="d"
  8. Title="JsonWindow" Height="450" Width="800" WindowStyle="ToolWindow">
  9. <Grid>
  10. <Grid.ColumnDefinitions>
  11. <ColumnDefinition Width="190*"/>
  12. <ColumnDefinition Width="167*"/>
  13. </Grid.ColumnDefinitions>
  14.  
  15. <TreeView Name="JsonTree" Margin="5">
  16. <TreeView.ItemContainerStyle>
  17. <!--TargetType指定要作用的对象类型-->
  18. <Style TargetType="TreeViewItem">
  19. <Style.Resources>
  20. <!--选中且处于激活状态的样式-->
  21. <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="DodgerBlue"/>
  22. <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="White"/>
  23. <!--选中且处于非激活状态的样式-->
  24. <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="DodgerBlue"/>
  25. <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="White"/>
  26. </Style.Resources>
  27. <!--将一些TreeViewItem属性与数据对象属性做绑定(Mode=TwoWay)-->
  28. <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
  29. <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
  30. <!--TextBlock.Text与TreeNodeModel.NodeName做数据双向绑定-->
  31. <Setter Property="TextBlock.Text" Value="{Binding NodeName, Mode=TwoWay}"/>
  32. <!--为TargetType注册事件处理器-->
  33. <EventSetter Event="PreviewMouseRightButtonDown" Handler="JsonTreeItem_PreviewMouseRightButtonDown"/>
  34. <EventSetter Event="Selected" Handler="TreeViewItem_Selected"/>
  35. </Style>
  36. </TreeView.ItemContainerStyle>
  37. <TreeView.ItemTemplate>
  38. <!--ItemsSource:指定子节点字段名称-->
  39. <HierarchicalDataTemplate DataType="{x:Type local:TreeNodeModel}" ItemsSource="{Binding Children}">
  40. <TextBlock Text="{Binding Path=NodeName}"></TextBlock>
  41. </HierarchicalDataTemplate>
  42. </TreeView.ItemTemplate>
  43. <!--右键菜单-->
  44. <TreeView.ContextMenu>
  45. <ContextMenu>
  46. <MenuItem x:Name="ContextMenuAddNode" Header="添加元素" Click="ContextMenuAddNode_Click"/>
  47. <MenuItem x:Name="ContextMenuRemoveNode" Header="删除元素" Click="ContextMenuRemoveNode_Click"/>
  48. </ContextMenu>
  49. </TreeView.ContextMenu>
  50. </TreeView>
  51. </Grid>
  52. </Window>



xaml.cs


  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Data;
  9. using System.Windows.Documents;
  10. using System.Windows.Input;
  11. using System.Windows.Media;
  12. using System.Windows.Media.Imaging;
  13. using System.Windows.Shapes;
  14. using System.Windows.Controls.Primitives;
  15. using System.Reflection;
  16.  
  17. namespace DataEditor
  18. {
  19. /// <summary>
  20. /// JsonWindow.xaml 的交互逻辑
  21. /// </summary>
  22. public partial class JsonWindow : Window
  23. {
  24. public JsonWindow()
  25. {
  26. InitializeComponent();
  27. Initialize();
  28. }
  29.  
  30. private void Initialize()
  31. {
  32. ShowTestData();
  33. }
  34.  
  35. //显示测试数据
  36. private void ShowTestData()
  37. {
  38. TestTreeObject obj = new TestTreeObject();
  39. List<TreeNodeModel> root = TreeNodeModel.Convert(obj);
  40. JsonTree.ItemsSource = root;
  41. }
  42.  
  43. private TreeViewItem _selectedTreeViewItem;
  44.  
  45. //TreeView选择节点
  46. private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
  47. {
  48. _selectedTreeViewItem = e.OriginalSource as TreeViewItem;
  49. Console.WriteLine(_selectedTreeViewItem);
  50. }
  51.  
  52. //TreeView节点上点击右键
  53. private void JsonTreeItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
  54. {
  55. var treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
  56. if (treeViewItem != null)
  57. {
  58. treeViewItem.Focus();
  59. e.Handled = true;
  60. }
  61.  
  62. TreeNodeModel model = treeViewItem.DataContext as TreeNodeModel;
  63. this.ContextMenuAddNode.IsEnabled = model.IsArray;
  64. this.ContextMenuRemoveNode.IsEnabled = model.CanRemove;
  65. }
  66.  
  67. //向上搜索父控件
  68. static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
  69. {
  70. while (source != null && source.GetType() != typeof(T))
  71. source = VisualTreeHelper.GetParent(source);
  72. return source;
  73. }
  74.  
  75. //右键菜单-添加节点
  76. private void ContextMenuAddNode_Click(object sender, RoutedEventArgs e)
  77. {
  78. AddArrayNode();
  79. }
  80.  
  81. //右键菜单-删除节点
  82. private void ContextMenuRemoveNode_Click(object sender, RoutedEventArgs e)
  83. {
  84. RemoveArrayNode();
  85. }
  86.  
  87. //添加数组节点
  88. private void AddArrayNode()
  89. {
  90. TreeNodeModel selectedValue = JsonTree.SelectedValue as TreeNodeModel;
  91. if (selectedValue == null || !selectedValue.DataFieldInfo.IsArray())
  92. return;
  93. if (selectedValue.Children == null)
  94. selectedValue.Children = new List<TreeNodeModel>();
  95. int childrenCount = selectedValue.Children.Count;
  96.  
  97. TreeNodeModel newModel = new TreeNodeModel();
  98. newModel.CanRemove = true;
  99. newModel.NodeName = string.Format("[{0}]", childrenCount);
  100. if (selectedValue.DataFieldInfo.IsJsonObjectArray())
  101. {
  102. Type elementType = selectedValue.DataFieldInfo.GetElementType();
  103. Console.WriteLine("Add Array Element: {0}", elementType.FullName);
  104. var newElement = Activator.CreateInstance(elementType);
  105. newModel.Children = TreeNodeModel.Convert(newElement);
  106. newModel.IsExpanded = false;
  107. newModel.IsSelected = false;
  108. }
  109. selectedValue.Children.Add(newModel);
  110. newModel.Parent = selectedValue;
  111. TreeViewRefresh();
  112. }
  113.  
  114. //删除数组节点
  115. private void RemoveArrayNode()
  116. {
  117. TreeNodeModel selectedValue = JsonTree.SelectedValue as TreeNodeModel;
  118. if (selectedValue == null || !selectedValue.CanRemove)
  119. return;
  120. selectedValue.Parent.Children.Remove(selectedValue);
  121. List<TreeNodeModel> children = selectedValue.Parent.Children;
  122. //重置索引节点编号
  123. for (int i=0; i<children.Count; i++) {
  124. var model = children[i];
  125. model.NodeName = string.Format("[{0}]", i);
  126. }
  127. selectedValue.Parent.IsSelected = true;
  128. TreeViewRefresh();
  129. }
  130.  
  131. //TreeView刷新显示
  132. private void TreeViewRefresh()
  133. {
  134. //刷新数据
  135. JsonTree.Items.Refresh();
  136. //判断节点生成器状态
  137. if (JsonTree.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
  138. JsonTree.UpdateLayout();
  139. }
  140. }
  141.  
  142. //TreeView的显示模型
  143. public class TreeNodeModel
  144. {
  145. //节点名称
  146. private string _NodeName;
  147. public string NodeName {
  148. get {
  149. if (DataFieldInfo.IsArray())
  150. {
  151. int childCount = Children != null ? Children.Count : 0;
  152. return string.Format("{0} [{1}]", _NodeName, childCount);
  153. }
  154. return _NodeName;
  155. }
  156. set { _NodeName = value; }
  157. }
  158. //是否展开
  159. public bool IsExpanded { get; set; }
  160. //是否选中
  161. public bool IsSelected { get; set; }
  162. //是否为可删除结点
  163. public bool CanRemove { get; set; }
  164. //是否为数组
  165. public bool IsArray { get; set; }
  166. public object DataObject { get; set; }
  167. public FieldInfo DataFieldInfo { get; set; }
  168. public PropertyInfo DataPropertyInfo { get; set; }
  169. public List<TreeNodeModel> Children { get; set; }
  170. public TreeNodeModel Parent { get; set; }
  171.  
  172. public void SetValue(object value)
  173. {
  174. DataPropertyInfo.SetValue(DataObject, value);
  175. }
  176.  
  177. public override string ToString()
  178. {
  179. return string.Format("NodeName={0}, ChildrenCount={1}",
  180. NodeName, Children == null ? 0 : Children.Count);
  181. }
  182.  
  183. //数据模型转显示模型
  184. public static List<TreeNodeModel> Convert(Object obj)
  185. {
  186. List<TreeNodeModel> list = new List<TreeNodeModel>();
  187. Type t = obj.GetType();
  188. return Convert(t);
  189. }
  190.  
  191. private static List<TreeNodeModel> Convert(Type t, TreeNodeModel parent=null)
  192. {
  193. List<TreeNodeModel> list = new List<TreeNodeModel>();
  194. FieldInfo[] fis = t.GetFields(BindingFlags.Instance | BindingFlags.Public);
  195. for (int i = 0; i < fis.Length; i++)
  196. {
  197. FieldInfo fi = fis[i];
  198. object[] attrs = fi.GetCustomAttributes(typeof(DescriptionAttribute), true);
  199. if (attrs == null || attrs.Length < 1)
  200. continue;
  201. DescriptionAttribute datt = (DescriptionAttribute)attrs[0];
  202. string desc = datt.description;
  203.  
  204. TreeNodeModel model = new TreeNodeModel();
  205. model.Parent = parent;
  206. model.NodeName = desc;
  207. model.DataFieldInfo = fi;
  208. model.IsArray = fi.IsArray();
  209. if (parent != null && parent.IsArray)
  210. model.CanRemove = true;
  211. list.Add(model);
  212.  
  213. //Type ft = fi.FieldType;
  214. //Console.WriteLine("FieldType: {0}", fi.FieldType);
  215.  
  216. if (fi.IsInt32())
  217. {
  218. //Console.WriteLine("{0} is Int32", fi.Name);
  219. }
  220.  
  221. if (fi.IsArray())
  222. {
  223. //Console.WriteLine("{0} is Array", fi.Name);
  224. }
  225.  
  226. if (fi.IsString())
  227. {
  228. //Console.WriteLine("{0} is String", fi.Name);
  229. }
  230.  
  231. if (fi.IsObject())
  232. {
  233. //Console.WriteLine("{0} is Object", fi.Name);
  234. }
  235.  
  236. if (fi.FieldType.BaseType == typeof(JsonObject))
  237. {
  238. //Console.WriteLine("{0} is JsonObject", fi.Name);
  239. model.Children = Convert(fi.FieldType, model);
  240. }
  241. }
  242. return list;
  243. }
  244. }
  245.  
  246. [Serializable]
  247. public class JsonObject { }
  248.  
  249. [Serializable]
  250. public class TestTreeObject : JsonObject
  251. {
  252. [Description("ID")]
  253. public int id;
  254. [Description("名称")]
  255. public string name;
  256. [Description("时间")]
  257. public TestTimebject time;
  258. [Description("时间数组")]
  259. public TestTimebject[] times;
  260. [Description("字符串数组")]
  261. public string[] arr;
  262. }
  263.  
  264. [Serializable]
  265. public class TestTimebject : JsonObject
  266. {
  267. [Description("时")]
  268. public int hour;
  269. [Description("分")]
  270. public int minute;
  271. [Description("秒")]
  272. public int second;
  273. }
  274. }



  1. using System;
  2. using System.Reflection;
  3.  
  4. namespace DataEditor
  5. {
  6. /// <summary>
  7. /// FieldInfo扩展方法
  8. /// </summary>
  9. internal static class FieldInfoExtensions
  10. {
  11. public static bool IsInt32(this FieldInfo fi)
  12. {
  13. if (fi == null)
  14. return false;
  15. return fi.FieldType == typeof(Int32);
  16. }
  17.  
  18. public static bool IsString(this FieldInfo fi)
  19. {
  20. if (fi == null)
  21. return false;
  22. return fi.FieldType == typeof(String);
  23. }
  24.  
  25. public static bool IsArray(this FieldInfo fi)
  26. {
  27. if (fi == null)
  28. return false;
  29. return fi.FieldType.BaseType == typeof(Array);
  30. }
  31.  
  32. public static bool IsObject(this FieldInfo fi)
  33. {
  34. if (fi == null)
  35. return false;
  36. return fi.FieldType.BaseType == typeof(Object);
  37. }
  38.  
  39. public static bool IsJsonObject(this FieldInfo fi)
  40. {
  41. if (fi == null)
  42. return false;
  43. return fi.FieldType.BaseType == typeof(JsonObject);
  44. }
  45.  
  46. public static bool IsJsonObjectArray(this FieldInfo fi)
  47. {
  48. if (!fi.IsArray())
  49. return false;
  50. Type t = fi.GetElementType();
  51. return t.IsSubclassOf(typeof(JsonObject));
  52. }
  53.  
  54. public static Type GetElementType(this FieldInfo fi)
  55. {
  56. Type elType = fi.FieldType;
  57. string typeName = string.Empty;
  58. if (fi.IsArray())
  59. {
  60. typeName = fi.FieldType.FullName.Replace("[]", string.Empty);
  61. elType = fi.FieldType.Assembly.GetType(typeName);
  62. }
  63. return elType;
  64. }
  65. }
  66. }


运行测试
将数据对象转成TreeView显示,且支持对数组进行添加/删除节点
1111.png

标签: C#

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号