WPF—TreeView
作者:追风剑情 发布于:2020-12-23 17:44 分类:C#
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;
}
}
}
运行测试
将数据对象转成TreeView显示,且支持对数组进行添加/删除节点
标签: C#
« 模型粒子化效果
|
UGUI—实时频谱效果»
日历
最新文章
随机文章
热门文章
分类
存档
- 2025年11月(1)
- 2025年9月(3)
- 2025年7月(4)
- 2025年6月(5)
- 2025年5月(1)
- 2025年4月(5)
- 2025年3月(4)
- 2025年2月(3)
- 2025年1月(1)
- 2024年12月(5)
- 2024年11月(5)
- 2024年10月(5)
- 2024年9月(3)
- 2024年8月(3)
- 2024年7月(11)
- 2024年6月(3)
- 2024年5月(9)
- 2024年4月(10)
- 2024年3月(11)
- 2024年2月(24)
- 2024年1月(12)
- 2023年12月(3)
- 2023年11月(9)
- 2023年10月(7)
- 2023年9月(2)
- 2023年8月(7)
- 2023年7月(9)
- 2023年6月(6)
- 2023年5月(7)
- 2023年4月(11)
- 2023年3月(6)
- 2023年2月(11)
- 2023年1月(8)
- 2022年12月(2)
- 2022年11月(4)
- 2022年10月(10)
- 2022年9月(2)
- 2022年8月(13)
- 2022年7月(7)
- 2022年6月(11)
- 2022年5月(18)
- 2022年4月(29)
- 2022年3月(5)
- 2022年2月(6)
- 2022年1月(8)
- 2021年12月(5)
- 2021年11月(3)
- 2021年10月(4)
- 2021年9月(9)
- 2021年8月(14)
- 2021年7月(8)
- 2021年6月(5)
- 2021年5月(2)
- 2021年4月(3)
- 2021年3月(7)
- 2021年2月(2)
- 2021年1月(8)
- 2020年12月(7)
- 2020年11月(2)
- 2020年10月(6)
- 2020年9月(9)
- 2020年8月(10)
- 2020年7月(9)
- 2020年6月(18)
- 2020年5月(4)
- 2020年4月(25)
- 2020年3月(38)
- 2020年1月(21)
- 2019年12月(13)
- 2019年11月(29)
- 2019年10月(44)
- 2019年9月(17)
- 2019年8月(18)
- 2019年7月(25)
- 2019年6月(25)
- 2019年5月(17)
- 2019年4月(10)
- 2019年3月(36)
- 2019年2月(35)
- 2019年1月(28)
- 2018年12月(30)
- 2018年11月(22)
- 2018年10月(4)
- 2018年9月(7)
- 2018年8月(13)
- 2018年7月(13)
- 2018年6月(6)
- 2018年5月(5)
- 2018年4月(13)
- 2018年3月(5)
- 2018年2月(3)
- 2018年1月(8)
- 2017年12月(35)
- 2017年11月(17)
- 2017年10月(16)
- 2017年9月(17)
- 2017年8月(20)
- 2017年7月(34)
- 2017年6月(17)
- 2017年5月(15)
- 2017年4月(32)
- 2017年3月(8)
- 2017年2月(2)
- 2017年1月(5)
- 2016年12月(14)
- 2016年11月(26)
- 2016年10月(12)
- 2016年9月(25)
- 2016年8月(32)
- 2016年7月(14)
- 2016年6月(21)
- 2016年5月(17)
- 2016年4月(13)
- 2016年3月(8)
- 2016年2月(8)
- 2016年1月(18)
- 2015年12月(13)
- 2015年11月(15)
- 2015年10月(12)
- 2015年9月(18)
- 2015年8月(21)
- 2015年7月(35)
- 2015年6月(13)
- 2015年5月(9)
- 2015年4月(4)
- 2015年3月(5)
- 2015年2月(4)
- 2015年1月(13)
- 2014年12月(7)
- 2014年11月(5)
- 2014年10月(4)
- 2014年9月(8)
- 2014年8月(16)
- 2014年7月(26)
- 2014年6月(22)
- 2014年5月(28)
- 2014年4月(15)
友情链接
- Unity官网
- Unity圣典
- Unity在线手册
- Unity中文手册(圣典)
- Unity官方中文论坛
- Unity游戏蛮牛用户文档
- Unity下载存档
- Unity引擎源码下载
- Unity服务
- Unity Ads
- wiki.unity3d
- Visual Studio Code官网
- SenseAR开发文档
- MSDN
- C# 参考
- C# 编程指南
- .NET Framework类库
- .NET 文档
- .NET 开发
- WPF官方文档
- uLua
- xLua
- SharpZipLib
- Protobuf-net
- Protobuf.js
- OpenSSL
- OPEN CASCADE
- JSON
- MessagePack
- C在线工具
- 游戏蛮牛
- GreenVPN
- 聚合数据
- 热云
- 融云
- 腾讯云
- 腾讯开放平台
- 腾讯游戏服务
- 腾讯游戏开发者平台
- 腾讯课堂
- 微信开放平台
- 腾讯实时音视频
- 腾讯即时通信IM
- 微信公众平台技术文档
- 白鹭引擎官网
- 白鹭引擎开放平台
- 白鹭引擎开发文档
- FairyGUI编辑器
- PureMVC-TypeScript
- 讯飞开放平台
- 亲加通讯云
- Cygwin
- Mono开发者联盟
- Scut游戏服务器引擎
- KBEngine游戏服务器引擎
- Photon游戏服务器引擎
- 码云
- SharpSvn
- 腾讯bugly
- 4399原创平台
- 开源中国
- Firebase
- Firebase-Admob-Unity
- google-services-unity
- Firebase SDK for Unity
- Google-Firebase-SDK
- AppsFlyer SDK
- android-repository
- CQASO
- Facebook开发者平台
- gradle下载
- GradleBuildTool下载
- Android Developers
- Google中国开发者
- AndroidDevTools
- Android社区
- Android开发工具
- Google Play Games Services
- Google商店
- Google APIs for Android
- 金钱豹VPN
- TouchSense SDK
- MakeHuman
- Online RSA Key Converter
- Windows UWP应用
- Visual Studio For Unity
- Open CASCADE Technology
- 慕课网
- 阿里云服务器ECS
- 在线免费文字转语音系统
- AI Studio
- 网云穿
- 百度网盘开放平台
- 迅捷画图
- 菜鸟工具
- [CSDN] 程序员研修院
- 华为人脸识别
- 百度AR导航导览SDK
- 海康威视官网
- 海康开放平台
- 海康SDK下载
- git download
- Open CASCADE
- CascadeStudio
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号






