鸟语天空
WPF—TreeView
post by:追风剑情 2020-12-23 17:44

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显示,且支持对数组进行添加/删除节点
1111.png

评论:
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容