xaml
<Window x:Class="DataEditor.JsonWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DataEditor" mc:Ignorable="d" Title="JsonWindow" Height="450" Width="800" WindowStyle="ToolWindow"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="190*"/> <ColumnDefinition Width="167*"/> </Grid.ColumnDefinitions> <TreeView Name="JsonTree" Margin="5"> <TreeView.ItemContainerStyle> <!--TargetType指定要作用的对象类型--> <Style TargetType="TreeViewItem"> <Style.Resources> <!--选中且处于激活状态的样式--> <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="DodgerBlue"/> <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="White"/> <!--选中且处于非激活状态的样式--> <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="DodgerBlue"/> <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="White"/> </Style.Resources> <!--将一些TreeViewItem属性与数据对象属性做绑定(Mode=TwoWay)--> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <!--TextBlock.Text与TreeNodeModel.NodeName做数据双向绑定--> <Setter Property="TextBlock.Text" Value="{Binding NodeName, Mode=TwoWay}"/> <!--为TargetType注册事件处理器--> <EventSetter Event="PreviewMouseRightButtonDown" Handler="JsonTreeItem_PreviewMouseRightButtonDown"/> <EventSetter Event="Selected" Handler="TreeViewItem_Selected"/> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <!--ItemsSource:指定子节点字段名称--> <HierarchicalDataTemplate DataType="{x:Type local:TreeNodeModel}" ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Path=NodeName}"></TextBlock> </HierarchicalDataTemplate> </TreeView.ItemTemplate> <!--右键菜单--> <TreeView.ContextMenu> <ContextMenu> <MenuItem x:Name="ContextMenuAddNode" Header="添加元素" Click="ContextMenuAddNode_Click"/> <MenuItem x:Name="ContextMenuRemoveNode" Header="删除元素" Click="ContextMenuRemoveNode_Click"/> </ContextMenu> </TreeView.ContextMenu> </TreeView> </Grid> </Window>
xaml.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Windows.Controls.Primitives; using System.Reflection; namespace DataEditor { /// <summary> /// JsonWindow.xaml 的交互逻辑 /// </summary> public partial class JsonWindow : Window { public JsonWindow() { InitializeComponent(); Initialize(); } private void Initialize() { ShowTestData(); } //显示测试数据 private void ShowTestData() { TestTreeObject obj = new TestTreeObject(); List<TreeNodeModel> root = TreeNodeModel.Convert(obj); JsonTree.ItemsSource = root; } private TreeViewItem _selectedTreeViewItem; //TreeView选择节点 private void TreeViewItem_Selected(object sender, RoutedEventArgs e) { _selectedTreeViewItem = e.OriginalSource as TreeViewItem; Console.WriteLine(_selectedTreeViewItem); } //TreeView节点上点击右键 private void JsonTreeItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { var treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem; if (treeViewItem != null) { treeViewItem.Focus(); e.Handled = true; } TreeNodeModel model = treeViewItem.DataContext as TreeNodeModel; this.ContextMenuAddNode.IsEnabled = model.IsArray; this.ContextMenuRemoveNode.IsEnabled = model.CanRemove; } //向上搜索父控件 static DependencyObject VisualUpwardSearch<T>(DependencyObject source) { while (source != null && source.GetType() != typeof(T)) source = VisualTreeHelper.GetParent(source); return source; } //右键菜单-添加节点 private void ContextMenuAddNode_Click(object sender, RoutedEventArgs e) { AddArrayNode(); } //右键菜单-删除节点 private void ContextMenuRemoveNode_Click(object sender, RoutedEventArgs e) { RemoveArrayNode(); } //添加数组节点 private void AddArrayNode() { TreeNodeModel selectedValue = JsonTree.SelectedValue as TreeNodeModel; if (selectedValue == null || !selectedValue.DataFieldInfo.IsArray()) return; if (selectedValue.Children == null) selectedValue.Children = new List<TreeNodeModel>(); int childrenCount = selectedValue.Children.Count; TreeNodeModel newModel = new TreeNodeModel(); newModel.CanRemove = true; newModel.NodeName = string.Format("[{0}]", childrenCount); if (selectedValue.DataFieldInfo.IsJsonObjectArray()) { Type elementType = selectedValue.DataFieldInfo.GetElementType(); Console.WriteLine("Add Array Element: {0}", elementType.FullName); var newElement = Activator.CreateInstance(elementType); newModel.Children = TreeNodeModel.Convert(newElement); newModel.IsExpanded = false; newModel.IsSelected = false; } selectedValue.Children.Add(newModel); newModel.Parent = selectedValue; TreeViewRefresh(); } //删除数组节点 private void RemoveArrayNode() { TreeNodeModel selectedValue = JsonTree.SelectedValue as TreeNodeModel; if (selectedValue == null || !selectedValue.CanRemove) return; selectedValue.Parent.Children.Remove(selectedValue); List<TreeNodeModel> children = selectedValue.Parent.Children; //重置索引节点编号 for (int i=0; i<children.Count; i++) { var model = children[i]; model.NodeName = string.Format("[{0}]", i); } selectedValue.Parent.IsSelected = true; TreeViewRefresh(); } //TreeView刷新显示 private void TreeViewRefresh() { //刷新数据 JsonTree.Items.Refresh(); //判断节点生成器状态 if (JsonTree.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) JsonTree.UpdateLayout(); } } //TreeView的显示模型 public class TreeNodeModel { //节点名称 private string _NodeName; public string NodeName { get { if (DataFieldInfo.IsArray()) { int childCount = Children != null ? Children.Count : 0; return string.Format("{0} [{1}]", _NodeName, childCount); } return _NodeName; } set { _NodeName = value; } } //是否展开 public bool IsExpanded { get; set; } //是否选中 public bool IsSelected { get; set; } //是否为可删除结点 public bool CanRemove { get; set; } //是否为数组 public bool IsArray { get; set; } public object DataObject { get; set; } public FieldInfo DataFieldInfo { get; set; } public PropertyInfo DataPropertyInfo { get; set; } public List<TreeNodeModel> Children { get; set; } public TreeNodeModel Parent { get; set; } public void SetValue(object value) { DataPropertyInfo.SetValue(DataObject, value); } public override string ToString() { return string.Format("NodeName={0}, ChildrenCount={1}", NodeName, Children == null ? 0 : Children.Count); } //数据模型转显示模型 public static List<TreeNodeModel> Convert(Object obj) { List<TreeNodeModel> list = new List<TreeNodeModel>(); Type t = obj.GetType(); return Convert(t); } private static List<TreeNodeModel> Convert(Type t, TreeNodeModel parent=null) { List<TreeNodeModel> list = new List<TreeNodeModel>(); FieldInfo[] fis = t.GetFields(BindingFlags.Instance | BindingFlags.Public); for (int i = 0; i < fis.Length; i++) { FieldInfo fi = fis[i]; object[] attrs = fi.GetCustomAttributes(typeof(DescriptionAttribute), true); if (attrs == null || attrs.Length < 1) continue; DescriptionAttribute datt = (DescriptionAttribute)attrs[0]; string desc = datt.description; TreeNodeModel model = new TreeNodeModel(); model.Parent = parent; model.NodeName = desc; model.DataFieldInfo = fi; model.IsArray = fi.IsArray(); if (parent != null && parent.IsArray) model.CanRemove = true; list.Add(model); //Type ft = fi.FieldType; //Console.WriteLine("FieldType: {0}", fi.FieldType); if (fi.IsInt32()) { //Console.WriteLine("{0} is Int32", fi.Name); } if (fi.IsArray()) { //Console.WriteLine("{0} is Array", fi.Name); } if (fi.IsString()) { //Console.WriteLine("{0} is String", fi.Name); } if (fi.IsObject()) { //Console.WriteLine("{0} is Object", fi.Name); } if (fi.FieldType.BaseType == typeof(JsonObject)) { //Console.WriteLine("{0} is JsonObject", fi.Name); model.Children = Convert(fi.FieldType, model); } } return list; } } [Serializable] public class JsonObject { } [Serializable] public class TestTreeObject : JsonObject { [Description("ID")] public int id; [Description("名称")] public string name; [Description("时间")] public TestTimebject time; [Description("时间数组")] public TestTimebject[] times; [Description("字符串数组")] public string[] arr; } [Serializable] public class TestTimebject : JsonObject { [Description("时")] public int hour; [Description("分")] public int minute; [Description("秒")] public int second; } }
using System; using System.Reflection; namespace DataEditor { /// <summary> /// FieldInfo扩展方法 /// </summary> internal static class FieldInfoExtensions { public static bool IsInt32(this FieldInfo fi) { if (fi == null) return false; return fi.FieldType == typeof(Int32); } public static bool IsString(this FieldInfo fi) { if (fi == null) return false; return fi.FieldType == typeof(String); } public static bool IsArray(this FieldInfo fi) { if (fi == null) return false; return fi.FieldType.BaseType == typeof(Array); } public static bool IsObject(this FieldInfo fi) { if (fi == null) return false; return fi.FieldType.BaseType == typeof(Object); } public static bool IsJsonObject(this FieldInfo fi) { if (fi == null) return false; return fi.FieldType.BaseType == typeof(JsonObject); } public static bool IsJsonObjectArray(this FieldInfo fi) { if (!fi.IsArray()) return false; Type t = fi.GetElementType(); return t.IsSubclassOf(typeof(JsonObject)); } public static Type GetElementType(this FieldInfo fi) { Type elType = fi.FieldType; string typeName = string.Empty; if (fi.IsArray()) { typeName = fi.FieldType.FullName.Replace("[]", string.Empty); elType = fi.FieldType.Assembly.GetType(typeName); } return elType; } } }