对象预览器支持了值类型集合成员的简单预览

This commit is contained in:
fengjiayi
2024-09-24 22:39:43 +08:00
parent 8a502b77d4
commit 06f6d2f34b
28 changed files with 1674 additions and 859 deletions

View File

@@ -1,7 +1,9 @@
using Serein.Library.Api;
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using Serein.NodeFlow.Base;
using Serein.NodeFlow.Tool.SereinExpression;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
@@ -14,6 +16,7 @@ using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup.Primitives;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
@@ -54,73 +57,83 @@ namespace Serein.WorkBench.Themes
/// </summary>
public partial class ObjectViewerControl : UserControl
{
private object _objectInstance;
public string NodeGuid { get;set; }
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
public IFlowEnvironment FlowEnvironment { get;set; }
public NodeModelBase NodeModel { get;set; }
public ObjectViewerControl()
{
InitializeComponent();
}
private DateTime _lastRefreshTime = DateTime.MinValue; // 上次刷新时间
private TimeSpan _refreshInterval = TimeSpan.FromSeconds(0.1); // 刷新间隔2秒
/// <summary>
/// 监视类型
/// </summary>
public enum MonitorType
{
/// <summary>
/// 作用于对象(对象的引用)的监视
/// </summary>
NodeFlowData,
/// <summary>
/// 作用与节点FLowData的监视
/// </summary>
IOCObj,
}
/// <summary>
/// 运行环境
/// </summary>
public IFlowEnvironment FlowEnvironment { get; set; }
/// <summary>
/// 监视对象的键
/// </summary>
public object MonitorKey { get => monitorKey; }
/// <summary>
/// 正在监视的对象
/// </summary>
public object MonitorObj { get => monitorObj; }
/// <summary>
/// 监视表达式
/// </summary>
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
private object monitorKey;
private object monitorObj;
// 用于存储当前展开的节点路径
private HashSet<string> expandedNodePaths = new HashSet<string>();
/// <summary>
/// 加载对象信息,展示其成员
/// </summary>
/// <param name="obj">要展示的对象</param>
public void LoadObjectInformation(object obj)
public void LoadObjectInformation(object key, object obj)
{
if (obj == null)
return;
// 当前时间
var currentTime = DateTime.Now;
// 如果上次刷新时间和当前时间之间的差值小于设定的间隔,则跳过
if (currentTime - _lastRefreshTime < _refreshInterval)
{
// 跳过过于频繁的刷新调用
return;
}
// 记录这次的刷新时间
_lastRefreshTime = currentTime;
_objectInstance = obj;
RefreshObjectTree(obj);
if (obj == null) return;
monitorKey = key;
monitorObj = obj;
expandedNodePaths.Clear();
LoadTree(obj);
}
///// <summary>
///// 添加表达式
///// </summary>
///// <param name="sender"></param>
///// <param name="e"></param>
//private void AddMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
//{
// OpenInputDialog((exp) =>
// {
// FlowEnvironment.AddInterruptExpression(NodeGuid, exp);
// });
//}
/// <summary>
/// 刷新对象
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
//RefreshObjectTree(_objectInstance);
FlowEnvironment.SetNodeFLowDataMonitorState(NodeGuid, true);
RefreshObjectTree(monitorObj);
}
/// <summary>
/// 更新表达式
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
{
//MonitorExpression = ExpressionTextBox.Text.ToString();
if(FlowEnvironment.AddInterruptExpression(NodeGuid, MonitorExpression))
if (FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
{
if (string.IsNullOrEmpty(MonitorExpression))
{
@@ -131,98 +144,72 @@ namespace Serein.WorkBench.Themes
UpMonitorExpressionButton.Content = "更新监视表达式";
}
}
}
// 用于存储当前展开的节点路径
private HashSet<string> _expandedNodePaths = new HashSet<string>();
private TreeViewItem? LoadTree(object obj)
{
if (obj is null) return null;
var objectType = obj.GetType();
FlowDataDetails flowDataDetails = new FlowDataDetails
{
Name = objectType.Name,
DataType = objectType,
DataValue = obj,
DataPath = ""
};
var rootNode = new TreeViewItem
{
Header = objectType.Name,
Tag = flowDataDetails,
};
ObjectTreeView.Items.Clear(); // 移除对象树的所有节点
ObjectTreeView.Items.Add(rootNode); // 添加所有节点
rootNode.Expanded += TreeViewItem_Expanded; // 监听展开事件
rootNode.Collapsed += TreeViewItem_Collapsed; // 监听折叠事件
// 这里创建了一个子项,并给这个子项创建了“正在加载”的子项
// 然后移除了原来对象树的所有项,再把这个新创建的子项添加上去
// 绑定了展开/折叠事件后自动展开第一层开始反射obj的成员并判断obj的成员生成什么样的节点
return rootNode;
}
/// <summary>
/// 刷新对象属性树
/// </summary>
public void RefreshObjectTree(object obj)
{
if (obj is null)
return;
// 当前时间
var currentTime = DateTime.Now;
// 如果上次刷新时间和当前时间之间的差值小于设定的间隔,则跳过
if (currentTime - _lastRefreshTime < _refreshInterval)
monitorObj = obj;
var rootNode = LoadTree(obj);
if (rootNode is not null)
{
// 跳过过于频繁的刷新调用
return;
ExpandPreviouslyExpandedNodes(rootNode); // 遍历节点,展开之前记录的节点
}
// 记录这次的刷新时间
_lastRefreshTime = currentTime;
var objectType = obj.GetType();
FlowDataDetails flowDataDetails = new FlowDataDetails
{
Name = objectType.Name,
DataType = objectType,
DataValue = obj
};
var rootNode = new TreeViewItem
{
Header = objectType.Name,
Tag = flowDataDetails,
};
// 添加占位符节点
AddPlaceholderNode(rootNode);
ObjectTreeView.Items.Clear();
ObjectTreeView.Items.Add(rootNode);
// 监听展开事件
rootNode.Expanded += TreeViewItem_Expanded;
// 自动展开第一层
rootNode.IsExpanded = true; // 直接展开根节点
// 加载根节点的属性和字段
if (rootNode.Items.Count == 1 && rootNode.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...")
{
rootNode.Items.Clear();
AddMembersToTreeNode(rootNode, obj, objectType);
}
// 遍历节点,展开之前记录的节点
ExpandPreviouslyExpandedNodes(rootNode);
}
// 遍历并展开之前记录的节点
private void ExpandPreviouslyExpandedNodes(TreeViewItem node)
/// <summary>
/// 展开父节点,如果路径存在哈希记录,则将其自动展开,并递归展开后的子节点。
/// </summary>
/// <param name="node"></param>
private void ExpandPreviouslyExpandedNodes(TreeViewItem node)
{
if (_expandedNodePaths.Contains(GetNodeFullPath(node)))
if (node == null) return;
if(node.Tag is FlowDataDetails flowDataDetails)
{
node.IsExpanded = true;
if (expandedNodePaths.Contains(flowDataDetails.DataPath))
{
node.IsExpanded = true;
}
}
foreach (TreeViewItem child in node.Items)
{
ExpandPreviouslyExpandedNodes(child);
}
}
/// <summary>
/// 添加父节点
/// </summary>
/// <param name="node"></param>
private void AddPlaceholderNode(TreeViewItem node)
{
node.Items.Add(new TreeViewItem { Header = "Loading..." });
}
/// <summary>
/// 展开子项事件
/// </summary>
@@ -231,19 +218,45 @@ namespace Serein.WorkBench.Themes
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
var item = (TreeViewItem)sender;
if (item.Items.Count == 1 && item.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...")
// 判断有没有加载过
//if (item.Items.Count == 1 && item.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...")
if (item.Items.Count == 0)
{
item.Items.Clear();
if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj
{
// 记录当前节点的路径
_expandedNodePaths.Add(GetNodeFullPath(item));
var path = flowDataDetails.DataPath;
expandedNodePaths.Add(path);
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
}
}
}
/// <summary>
/// 折叠事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
{
var item = (TreeViewItem)sender;
if (item.Items.Count > 0)
{
if (item.Tag is FlowDataDetails flowDataDetails)
{
// 记录当前节点的路径
var path = flowDataDetails.DataPath;
if(path != "")
{
expandedNodePaths.Remove(path);
}
}
}
}
/// <summary>
/// 反射对象数据添加子节点
/// </summary>
@@ -252,81 +265,168 @@ namespace Serein.WorkBench.Themes
/// <param name="type"></param>
private void AddMembersToTreeNode(TreeViewItem treeViewNode, object obj, Type type)
{
// 获取属性和字段
// 获取公开的属性
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (var member in members)
{
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member);
treeViewNode.Items.Add(memberNode);
if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu))
if (member.Name.StartsWith(".") ||
member.Name.StartsWith("get_") ||
member.Name.StartsWith("set_")
)
{
memberNode.ContextMenu = contextMenu; // 设置子项节点的事件
// 跳过构造函数、属性的get/set方法
continue;
}
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;
}
}
}
/// <summary>
/// 配置右键菜单功能
/// 配置节点子项
/// </summary>
/// <param name="obj"></param>
/// <param name="member"></param>
/// <returns></returns>
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
{
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
#region
if (member is PropertyInfo property)
{
string propertyValue = GetPropertyValue(obj, property,out object value);
FlowDataDetails flowDataDetails = new FlowDataDetails
if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.GetValue(obj) is IEnumerable collection && collection is not null)
{
ItemType = TreeItemType.Property,
DataType = property.PropertyType,
Name = property.Name,
DataValue = value,
DataPath = GetNodeFullPath(memberNode),
};
// 处理集合类型的属性
memberNode.Tag = new FlowDataDetails
{
ItemType = TreeItemType.IEnumerable,
DataType = property.PropertyType,
Name = property.Name,
DataValue = collection,
};
memberNode.Tag = flowDataDetails;
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}";
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
{
AddPlaceholderNode(memberNode);
memberNode.Expanded += TreeViewItem_Expanded;
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 = property.Name,
DataValue = itemNode,
};
memberNode.Items.Add(itemNode);
}
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} [{index}]";
return memberNode;
}
else
{
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;
}
}
#endregion
#region
else if (member is FieldInfo field)
{
string fieldValue = GetFieldValue(obj, field, out object value);
FlowDataDetails flowDataDetails = new FlowDataDetails
if (typeof(IEnumerable).IsAssignableFrom(field.FieldType) && field.GetValue(obj) is IEnumerable collection && collection is not null)
{
ItemType = TreeItemType.Field,
DataType = field.FieldType,
Name = field.Name,
DataValue = value,
DataPath = GetNodeFullPath(memberNode),
};
memberNode.Tag = flowDataDetails;
memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}";
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
// 处理集合类型的字段
memberNode.Tag = new FlowDataDetails
{
ItemType = TreeItemType.IEnumerable,
DataType = field.FieldType,
Name = field.Name,
DataValue = collection,
};
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}]";
return memberNode;
}
else
{
AddPlaceholderNode(memberNode);
memberNode.Expanded += TreeViewItem_Expanded;
string fieldValue = GetFieldValue(obj, field, out object value);
memberNode.Tag = new FlowDataDetails
{
ItemType = TreeItemType.Field,
DataType = field.FieldType,
Name = field.Name,
DataValue = value,
};
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;
}
}
#endregion
#region null
else
{
return null;
}
#endregion
return memberNode;
}
/// <summary>
@@ -335,15 +435,12 @@ namespace Serein.WorkBench.Themes
/// <param name="obj"></param>
/// <param name="property"></param>
/// <returns></returns>
private string GetPropertyValue(object obj, PropertyInfo property,out object value)
private string GetPropertyValue(object obj, PropertyInfo property,out object value)
{
try
{
var properties = obj.GetType().GetProperties();
// 获取实例属性值
value = property.GetValue(obj);
return value?.ToString() ?? "null"; // 返回值或“null”
@@ -355,14 +452,13 @@ namespace Serein.WorkBench.Themes
}
}
/// <summary>
/// 获取字段类型的成员
/// </summary>
/// <param name="obj"></param>
/// <param name="field"></param>
/// <returns></returns>
private string GetFieldValue(object obj, FieldInfo field, out object value)
private string GetFieldValue(object obj, FieldInfo field, out object value)
{
try
{
@@ -376,165 +472,170 @@ namespace Serein.WorkBench.Themes
}
}
/// <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)
{
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>
/// 获取当前节点的完整路径,例如 "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 "";
// return typeNodeDetails.Name.ToString();
}
}
/// <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;
}
private InputDialog OpenInputDialog(Action<string> action)
{
var inputDialog = new InputDialog();
inputDialog.Closed += (s, e) =>
{
if (inputDialog.DialogResult == true)
{
string userInput = inputDialog.InputValue;
action?.Invoke(userInput);
}
};
inputDialog.ShowDialog();
return inputDialog;
}
///// <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;
// });
// }
//}
}
}
/// <summary>
/// 上次刷新事件
/// </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;
// });
// }
//}