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;
}
}
}