2024-09-24 22:39:43 +08:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
|
using Serein.Library.Api;
|
2024-09-22 17:37:32 +08:00
|
|
|
|
using Serein.NodeFlow.Base;
|
2024-09-22 14:10:13 +08:00
|
|
|
|
using Serein.NodeFlow.Tool.SereinExpression;
|
|
|
|
|
|
using System;
|
2024-09-24 22:39:43 +08:00
|
|
|
|
using System.Collections;
|
2024-09-22 14:10:13 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
|
using System.Reflection;
|
2024-09-22 17:37:32 +08:00
|
|
|
|
using System.Security.Cryptography;
|
2024-09-22 14:10:13 +08:00
|
|
|
|
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;
|
2024-09-24 22:39:43 +08:00
|
|
|
|
using System.Windows.Markup.Primitives;
|
2024-09-22 14:10:13 +08:00
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
|
using System.Windows.Media.Imaging;
|
|
|
|
|
|
using System.Windows.Navigation;
|
|
|
|
|
|
using System.Windows.Shapes;
|
|
|
|
|
|
using System.Xml.Linq;
|
|
|
|
|
|
using static Serein.WorkBench.Themes.TypeViewerWindow;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Serein.WorkBench.Themes
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
public class FlowDataDetails
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 属性名称
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string Name { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 属性类型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public TreeItemType ItemType { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据类型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Type DataType { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public object DataValue { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据路径
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string DataPath { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ObjectViewerControl.xaml 的交互逻辑
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public partial class ObjectViewerControl : UserControl
|
|
|
|
|
|
{
|
|
|
|
|
|
public ObjectViewerControl()
|
|
|
|
|
|
{
|
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// 监视类型
|
2024-09-22 14:10:13 +08:00
|
|
|
|
/// </summary>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
public enum MonitorType
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 作用于对象(对象的引用)的监视
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
NodeFlowData,
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 作用与节点(FLowData)的监视
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
IOCObj,
|
|
|
|
|
|
}
|
2024-09-22 17:37:32 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 运行环境
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public IFlowEnvironment FlowEnvironment { get; set; }
|
2024-09-22 17:37:32 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 监视对象的键
|
|
|
|
|
|
/// </summary>
|
2024-09-26 21:00:17 +08:00
|
|
|
|
public string MonitorKey { get => monitorKey; }
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 正在监视的对象
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public object MonitorObj { get => monitorObj; }
|
2024-09-22 17:37:32 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 监视表达式
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
|
2024-09-22 14:10:13 +08:00
|
|
|
|
|
2024-09-26 21:00:17 +08:00
|
|
|
|
private string monitorKey;
|
2024-09-24 22:39:43 +08:00
|
|
|
|
private object monitorObj;
|
2024-09-22 17:37:32 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
// 用于存储当前展开的节点路径
|
|
|
|
|
|
private HashSet<string> expandedNodePaths = new HashSet<string>();
|
|
|
|
|
|
|
2024-09-22 17:37:32 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 加载对象信息,展示其成员
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="obj">要展示的对象</param>
|
2024-09-26 21:00:17 +08:00
|
|
|
|
public void LoadObjectInformation(string key, object obj)
|
2024-09-24 22:39:43 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (obj == null) return;
|
|
|
|
|
|
monitorKey = key;
|
|
|
|
|
|
monitorObj = obj;
|
|
|
|
|
|
expandedNodePaths.Clear();
|
|
|
|
|
|
LoadTree(obj);
|
|
|
|
|
|
}
|
2024-09-22 17:37:32 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 刷新对象
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
|
/// <param name="e"></param>
|
2024-09-22 17:37:32 +08:00
|
|
|
|
private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
RefreshObjectTree(monitorObj);
|
2024-09-22 17:37:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新表达式
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
|
/// <param name="e"></param>
|
2024-09-22 17:37:32 +08:00
|
|
|
|
private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
if (FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
|
2024-09-22 17:37:32 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(MonitorExpression))
|
|
|
|
|
|
{
|
|
|
|
|
|
ExpressionTextBox.Text = "表达式已清空";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
UpMonitorExpressionButton.Content = "更新监视表达式";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
private TreeViewItem? LoadTree(object obj)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
if (obj is null) return null;
|
2024-09-22 14:10:13 +08:00
|
|
|
|
var objectType = obj.GetType();
|
|
|
|
|
|
FlowDataDetails flowDataDetails = new FlowDataDetails
|
|
|
|
|
|
{
|
|
|
|
|
|
Name = objectType.Name,
|
|
|
|
|
|
DataType = objectType,
|
2024-09-24 22:39:43 +08:00
|
|
|
|
DataValue = obj,
|
|
|
|
|
|
DataPath = ""
|
2024-09-22 14:10:13 +08:00
|
|
|
|
};
|
2024-09-24 22:39:43 +08:00
|
|
|
|
var rootNode = new TreeViewItem
|
|
|
|
|
|
{
|
|
|
|
|
|
Header = objectType.Name,
|
2024-09-22 14:10:13 +08:00
|
|
|
|
Tag = flowDataDetails,
|
|
|
|
|
|
};
|
2024-09-24 22:39:43 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ObjectTreeView.Items.Clear(); // 移除对象树的所有节点
|
|
|
|
|
|
ObjectTreeView.Items.Add(rootNode); // 添加所有节点
|
|
|
|
|
|
rootNode.Expanded += TreeViewItem_Expanded; // 监听展开事件
|
|
|
|
|
|
rootNode.Collapsed += TreeViewItem_Collapsed; // 监听折叠事件
|
|
|
|
|
|
// 这里创建了一个子项,并给这个子项创建了“正在加载”的子项
|
|
|
|
|
|
// 然后移除了原来对象树的所有项,再把这个新创建的子项添加上去
|
|
|
|
|
|
// 绑定了展开/折叠事件后,自动展开第一层,开始反射obj的成员,并判断obj的成员生成什么样的节点
|
2024-09-26 21:00:17 +08:00
|
|
|
|
rootNode.IsExpanded = true;
|
2024-09-24 22:39:43 +08:00
|
|
|
|
return rootNode;
|
|
|
|
|
|
}
|
2024-09-22 14:10:13 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 刷新对象属性树
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void RefreshObjectTree(object obj)
|
|
|
|
|
|
{
|
|
|
|
|
|
monitorObj = obj;
|
|
|
|
|
|
var rootNode = LoadTree(obj);
|
|
|
|
|
|
if (rootNode is not null)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
ExpandPreviouslyExpandedNodes(rootNode); // 遍历节点,展开之前记录的节点
|
|
|
|
|
|
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
2024-09-24 22:39:43 +08:00
|
|
|
|
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 展开父节点,如果路径存在哈希记录,则将其自动展开,并递归展开后的子节点。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="node"></param>
|
|
|
|
|
|
private void ExpandPreviouslyExpandedNodes(TreeViewItem node)
|
2024-09-22 17:37:32 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
if (node == null) return;
|
|
|
|
|
|
if(node.Tag is FlowDataDetails flowDataDetails)
|
2024-09-22 17:37:32 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
if (expandedNodePaths.Contains(flowDataDetails.DataPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
node.IsExpanded = true;
|
|
|
|
|
|
}
|
2024-09-22 17:37:32 +08:00
|
|
|
|
}
|
2024-09-24 22:39:43 +08:00
|
|
|
|
|
2024-09-22 17:37:32 +08:00
|
|
|
|
foreach (TreeViewItem child in node.Items)
|
|
|
|
|
|
{
|
|
|
|
|
|
ExpandPreviouslyExpandedNodes(child);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-09-22 14:10:13 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// 展开子项事件
|
2024-09-22 14:10:13 +08:00
|
|
|
|
/// </summary>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
|
/// <param name="e"></param>
|
|
|
|
|
|
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
if (sender is TreeViewItem item)
|
2024-09-24 22:39:43 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj
|
|
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
if (flowDataDetails.ItemType == TreeItemType.Item || item.Items.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 记录当前节点的路径
|
|
|
|
|
|
var path = flowDataDetails.DataPath;
|
|
|
|
|
|
expandedNodePaths.Add(path);
|
|
|
|
|
|
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// 折叠事件
|
2024-09-22 14:10:13 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
|
/// <param name="e"></param>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
if (sender is TreeViewItem item && item.Items.Count > 0)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
if (item.Tag is FlowDataDetails flowDataDetails)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-22 17:37:32 +08:00
|
|
|
|
// 记录当前节点的路径
|
2024-09-24 22:39:43 +08:00
|
|
|
|
var path = flowDataDetails.DataPath;
|
|
|
|
|
|
if(path != "")
|
|
|
|
|
|
{
|
|
|
|
|
|
expandedNodePaths.Remove(path);
|
|
|
|
|
|
}
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 反射对象数据添加子节点
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="treeViewNode"></param>
|
|
|
|
|
|
/// <param name="obj"></param>
|
|
|
|
|
|
/// <param name="type"></param>
|
2024-09-22 17:37:32 +08:00
|
|
|
|
private void AddMembersToTreeNode(TreeViewItem treeViewNode, object obj, Type type)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
// 获取公开的属性
|
2024-09-22 14:10:13 +08:00
|
|
|
|
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
|
|
|
|
|
foreach (var member in members)
|
|
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
if (member.Name.StartsWith(".") ||
|
|
|
|
|
|
member.Name.StartsWith("get_") ||
|
|
|
|
|
|
member.Name.StartsWith("set_")
|
|
|
|
|
|
)
|
2024-09-22 17:37:32 +08:00
|
|
|
|
{
|
2024-09-24 22:39:43 +08:00
|
|
|
|
// 跳过构造函数、属性的get/set方法
|
|
|
|
|
|
continue;
|
2024-09-22 17:37:32 +08:00
|
|
|
|
}
|
2024-09-22 14:10:13 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象
|
|
|
|
|
|
if (memberNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
treeViewNode.Items.Add(memberNode); // 添加到当前节点
|
|
|
|
|
|
|
|
|
|
|
|
// 配置数据路径
|
|
|
|
|
|
FlowDataDetails subFlowDataDetails = (FlowDataDetails)memberNode.Tag;
|
|
|
|
|
|
string superPath = ((FlowDataDetails)treeViewNode.Tag).DataPath;
|
|
|
|
|
|
string subPath = superPath + "." + subFlowDataDetails.Name;
|
|
|
|
|
|
subFlowDataDetails.DataPath = subPath;
|
|
|
|
|
|
|
|
|
|
|
|
// 配置右键菜单
|
|
|
|
|
|
var contextMenu = new ContextMenu();
|
|
|
|
|
|
contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ExpressionTextBox.Text = subPath; // 获取表达式
|
|
|
|
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
memberNode.ContextMenu = contextMenu;
|
|
|
|
|
|
}
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// 配置节点子项
|
2024-09-22 14:10:13 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="obj"></param>
|
|
|
|
|
|
/// <param name="member"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-30 02:49:00 +08:00
|
|
|
|
if (obj == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2024-09-24 22:39:43 +08:00
|
|
|
|
#region 属性
|
2024-09-22 14:10:13 +08:00
|
|
|
|
if (member is PropertyInfo property)
|
|
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
#region 集合类型
|
2024-09-24 22:39:43 +08:00
|
|
|
|
if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.GetValue(obj) is IEnumerable collection && collection is not null)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
2024-09-24 22:39:43 +08:00
|
|
|
|
// 处理集合类型的属性
|
|
|
|
|
|
memberNode.Tag = new FlowDataDetails
|
|
|
|
|
|
{
|
|
|
|
|
|
ItemType = TreeItemType.IEnumerable,
|
|
|
|
|
|
DataType = property.PropertyType,
|
|
|
|
|
|
Name = property.Name,
|
|
|
|
|
|
DataValue = collection,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int index = 0;
|
|
|
|
|
|
foreach (var item in collection)
|
|
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
var itemNode = new TreeViewItem { Header = $"[{index++}] {item}" ?? "null" };
|
2024-09-24 22:39:43 +08:00
|
|
|
|
memberNode.Tag = new FlowDataDetails
|
|
|
|
|
|
{
|
|
|
|
|
|
ItemType = TreeItemType.Item,
|
|
|
|
|
|
DataType = item?.GetType(),
|
|
|
|
|
|
Name = property.Name,
|
|
|
|
|
|
DataValue = itemNode,
|
|
|
|
|
|
};
|
|
|
|
|
|
memberNode.Items.Add(itemNode);
|
|
|
|
|
|
}
|
|
|
|
|
|
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} [{index}]";
|
2024-09-24 22:55:09 +08:00
|
|
|
|
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
|
|
|
|
|
|
{
|
|
|
|
|
|
memberNode.Expanded += TreeViewItem_Expanded;
|
|
|
|
|
|
memberNode.Collapsed += TreeViewItem_Collapsed;
|
|
|
|
|
|
}
|
2024-09-24 22:39:43 +08:00
|
|
|
|
return memberNode;
|
|
|
|
|
|
}
|
2024-09-24 22:55:09 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
#region 值类型与未判断的类型
|
2024-09-24 22:39:43 +08:00
|
|
|
|
else
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
2024-09-24 22:39:43 +08:00
|
|
|
|
string propertyValue = GetPropertyValue(obj, property, out object value);
|
|
|
|
|
|
memberNode.Tag = new FlowDataDetails
|
|
|
|
|
|
{
|
|
|
|
|
|
ItemType = TreeItemType.Property,
|
|
|
|
|
|
DataType = property.PropertyType,
|
|
|
|
|
|
Name = property.Name,
|
|
|
|
|
|
DataValue = value,
|
|
|
|
|
|
}; ;
|
|
|
|
|
|
|
|
|
|
|
|
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}";
|
|
|
|
|
|
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
|
|
|
|
|
|
{
|
|
|
|
|
|
memberNode.Expanded += TreeViewItem_Expanded;
|
|
|
|
|
|
memberNode.Collapsed += TreeViewItem_Collapsed;
|
|
|
|
|
|
}
|
|
|
|
|
|
return memberNode;
|
2024-09-24 22:55:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
2024-09-24 22:39:43 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
#region 字段
|
2024-09-22 14:10:13 +08:00
|
|
|
|
else if (member is FieldInfo field)
|
|
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
#region 集合类型
|
2024-09-24 22:39:43 +08:00
|
|
|
|
if (typeof(IEnumerable).IsAssignableFrom(field.FieldType) && field.GetValue(obj) is IEnumerable collection && collection is not null)
|
|
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
2024-09-24 22:39:43 +08:00
|
|
|
|
// 处理集合类型的字段
|
|
|
|
|
|
memberNode.Tag = new FlowDataDetails
|
|
|
|
|
|
{
|
|
|
|
|
|
ItemType = TreeItemType.IEnumerable,
|
|
|
|
|
|
DataType = field.FieldType,
|
|
|
|
|
|
Name = field.Name,
|
|
|
|
|
|
DataValue = collection,
|
|
|
|
|
|
};
|
2024-09-24 22:55:09 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
int index = 0;
|
|
|
|
|
|
foreach (var item in collection)
|
|
|
|
|
|
{
|
|
|
|
|
|
var itemNode = new TreeViewItem { Header = $"[{index++}] {item}" ?? "null" };
|
|
|
|
|
|
memberNode.Tag = new FlowDataDetails
|
|
|
|
|
|
{
|
|
|
|
|
|
ItemType = TreeItemType.Item,
|
|
|
|
|
|
DataType = item?.GetType(),
|
|
|
|
|
|
Name = field.Name,
|
|
|
|
|
|
DataValue = itemNode,
|
|
|
|
|
|
};
|
|
|
|
|
|
//collectionNode.Items.Add(itemNode);
|
|
|
|
|
|
memberNode.Items.Add(itemNode);
|
|
|
|
|
|
}
|
|
|
|
|
|
memberNode.Header = $"{field.Name} : {field.FieldType.Name} [{index}]";
|
2024-09-24 22:55:09 +08:00
|
|
|
|
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
|
|
|
|
|
|
{
|
|
|
|
|
|
memberNode.Expanded += TreeViewItem_Expanded;
|
|
|
|
|
|
memberNode.Collapsed += TreeViewItem_Collapsed;
|
|
|
|
|
|
}
|
2024-09-24 22:39:43 +08:00
|
|
|
|
return memberNode;
|
|
|
|
|
|
}
|
2024-09-24 22:55:09 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
#region 值类型与未判断的类型
|
2024-09-24 22:39:43 +08:00
|
|
|
|
else
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
2024-09-24 22:55:09 +08:00
|
|
|
|
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
2024-09-24 22:39:43 +08:00
|
|
|
|
string fieldValue = GetFieldValue(obj, field, out object value);
|
2024-09-22 14:10:13 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
memberNode.Tag = new FlowDataDetails
|
|
|
|
|
|
{
|
|
|
|
|
|
ItemType = TreeItemType.Field,
|
|
|
|
|
|
DataType = field.FieldType,
|
|
|
|
|
|
Name = field.Name,
|
|
|
|
|
|
DataValue = value,
|
2024-09-22 14:10:13 +08:00
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
};
|
|
|
|
|
|
memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}";
|
|
|
|
|
|
|
|
|
|
|
|
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
|
|
|
|
|
|
{
|
|
|
|
|
|
memberNode.Expanded += TreeViewItem_Expanded;
|
|
|
|
|
|
memberNode.Collapsed += TreeViewItem_Collapsed;
|
|
|
|
|
|
}
|
|
|
|
|
|
return memberNode;
|
2024-09-24 22:55:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
2024-09-24 22:39:43 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
#region 返回null
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
2024-09-22 14:10:13 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取属性类型的成员
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="obj"></param>
|
|
|
|
|
|
/// <param name="property"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
private string GetPropertyValue(object obj, PropertyInfo property,out object value)
|
2024-09-22 17:37:32 +08:00
|
|
|
|
{
|
2024-09-22 14:10:13 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2024-09-26 21:00:17 +08:00
|
|
|
|
if(obj is null)
|
|
|
|
|
|
{
|
|
|
|
|
|
value = null;
|
|
|
|
|
|
return "Error";
|
|
|
|
|
|
}
|
2024-09-22 17:37:32 +08:00
|
|
|
|
var properties = obj.GetType().GetProperties();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取实例属性值
|
|
|
|
|
|
value = property.GetValue(obj);
|
|
|
|
|
|
return value?.ToString() ?? "null"; // 返回值或“null”
|
2024-09-22 14:10:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
2024-09-22 17:37:32 +08:00
|
|
|
|
value = null;
|
2024-09-22 14:10:13 +08:00
|
|
|
|
return "Error";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取字段类型的成员
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="obj"></param>
|
|
|
|
|
|
/// <param name="field"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-09-24 22:39:43 +08:00
|
|
|
|
private string GetFieldValue(object obj, FieldInfo field, out object value)
|
2024-09-22 14:10:13 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2024-09-22 17:37:32 +08:00
|
|
|
|
value = field.GetValue(obj);
|
2024-09-22 14:10:13 +08:00
|
|
|
|
return value?.ToString() ?? "null";
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
2024-09-22 17:37:32 +08:00
|
|
|
|
value = null;
|
2024-09-22 14:10:13 +08:00
|
|
|
|
return "Error";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// <summary>
|
2024-09-25 22:20:23 +08:00
|
|
|
|
/// 上次刷新时间
|
2024-09-24 22:39:43 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
//private DateTime lastRefreshTime = DateTime.MinValue;
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 刷新间隔
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
//private readonly TimeSpan refreshInterval = TimeSpan.FromSeconds(0.1);
|
|
|
|
|
|
// 当前时间
|
|
|
|
|
|
//var currentTime = DateTime.Now;
|
|
|
|
|
|
//if (currentTime - lastRefreshTime < refreshInterval)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// return; // 跳过过于频繁的刷新调用
|
|
|
|
|
|
//}
|
|
|
|
|
|
//else
|
|
|
|
|
|
//{
|
|
|
|
|
|
// lastRefreshTime = currentTime;// 记录这次的刷新时间
|
|
|
|
|
|
//}
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从当前节点获取至父节点的路径,例如 "node1.node2.node3.node4"
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="node">目标节点</param>
|
|
|
|
|
|
/// <returns>节点路径</returns>
|
|
|
|
|
|
//private string GetNodeFullPath(TreeViewItem node)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// if (node == null)
|
|
|
|
|
|
// return string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
// FlowDataDetails flowDataDetails = (FlowDataDetails)node.Tag;
|
|
|
|
|
|
// var parent = GetParentTreeViewItem(node);
|
|
|
|
|
|
// if (parent != null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// // 递归获取父节点的路径,并拼接当前节点的 Header
|
|
|
|
|
|
// return $"{GetNodeFullPath(parent)}.{flowDataDetails.Name}";
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// // 没有父节点,则说明这是根节点,直接返回 Header
|
|
|
|
|
|
// return "";
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取指定节点的父级节点
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="node">目标节点</param>
|
|
|
|
|
|
/// <returns>父节点</returns>
|
|
|
|
|
|
//private TreeViewItem GetParentTreeViewItem(TreeViewItem node)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// DependencyObject parent = VisualTreeHelper.GetParent(node);
|
|
|
|
|
|
// while (parent != null && !(parent is TreeViewItem))
|
|
|
|
|
|
// {
|
|
|
|
|
|
// parent = VisualTreeHelper.GetParent(parent);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return parent as TreeViewItem;
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据成员类别配置右键菜单
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="memberNode"></param>
|
|
|
|
|
|
/// <param name="member"></param>
|
|
|
|
|
|
/// <param name="contextMenu"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
//private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member, out ContextMenu? contextMenu)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu))
|
|
|
|
|
|
// {
|
|
|
|
|
|
// memberNode.ContextMenu = contextMenu; // 设置子项节点的事件
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// bool isChange = false;
|
|
|
|
|
|
// if (member is PropertyInfo property)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// isChange = true;
|
|
|
|
|
|
// contextMenu = new ContextMenu();
|
|
|
|
|
|
// contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
|
|
|
|
|
// {
|
|
|
|
|
|
// string fullPath = GetNodeFullPath(memberNode);
|
|
|
|
|
|
// string copyValue = /*"@Get " + */fullPath;
|
|
|
|
|
|
// ExpressionTextBox.Text = copyValue;
|
|
|
|
|
|
// // Clipboard.SetDataObject(copyValue);
|
|
|
|
|
|
|
|
|
|
|
|
// }));
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else if (member is MethodInfo method)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// //isChange = true;
|
|
|
|
|
|
// contextMenu = new ContextMenu();
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else if (member is FieldInfo field)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// isChange = true;
|
|
|
|
|
|
// contextMenu = new ContextMenu();
|
|
|
|
|
|
// contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
|
|
|
|
|
// {
|
|
|
|
|
|
// string fullPath = GetNodeFullPath(memberNode);
|
|
|
|
|
|
// string copyValue = /*"@Get " +*/ fullPath;
|
|
|
|
|
|
// ExpressionTextBox.Text = copyValue;
|
|
|
|
|
|
// // Clipboard.SetDataObject(copyValue);
|
|
|
|
|
|
// }));
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// contextMenu = new ContextMenu();
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return isChange;
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///// <summary>
|
|
|
|
|
|
///// 刷新按钮的点击事件
|
|
|
|
|
|
///// </summary>
|
|
|
|
|
|
//private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// RefreshObjectTree();
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
//private bool IsTimerRefres = false;
|
|
|
|
|
|
//private void TimerRefreshButton_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// if (IsTimerRefres)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// IsTimerRefres = false;
|
|
|
|
|
|
// TimerRefreshButton.Content = "定时刷新";
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// IsTimerRefres = true;
|
|
|
|
|
|
// TimerRefreshButton.Content = "取消刷新";
|
|
|
|
|
|
|
|
|
|
|
|
// _ = Task.Run(async () => {
|
|
|
|
|
|
// while (true)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// if (IsTimerRefres)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// Application.Current.Dispatcher.Invoke(() =>
|
|
|
|
|
|
// {
|
|
|
|
|
|
// RefreshObjectTree(); // 刷新UI
|
|
|
|
|
|
// });
|
|
|
|
|
|
// await Task.Delay(100);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
// IsTimerRefres = false;
|
|
|
|
|
|
// });
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|