Files
Ramitta-lib/Ramitta/winTreeList.xaml.cs

646 lines
19 KiB
C#
Raw Normal View History

2025-09-02 18:41:07 +08:00
using Newtonsoft.Json.Linq;
using System;
2025-08-29 14:57:55 +08:00
using System.Collections.Generic;
using System.Collections.ObjectModel;
2025-09-03 10:34:45 +08:00
using System.ComponentModel;
2025-08-29 14:57:55 +08:00
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Channels;
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.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Xml.Linq;
2025-09-03 10:34:45 +08:00
using static System.Net.Mime.MediaTypeNames;
2025-09-02 18:41:07 +08:00
using static System.Runtime.InteropServices.JavaScript.JSType;
2025-08-29 14:57:55 +08:00
namespace Ramitta
{
2025-09-03 10:34:45 +08:00
public partial class winTreeList : UserControl, INotifyPropertyChanged
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
#region
2025-09-03 10:34:45 +08:00
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
2025-08-29 14:57:55 +08:00
public winTreeList()
{
InitializeComponent();
Nodes = new ObservableCollection<TreeNode>();
}
2025-09-03 16:16:00 +08:00
#endregion
2025-08-29 14:57:55 +08:00
#region
/// <summary>
/// 获取或设置树形列表的节点集合
/// </summary>
public ObservableCollection<TreeNode> Nodes
{
get { return (ObservableCollection<TreeNode>)GetValue(NodesProperty); }
set { SetValue(NodesProperty, value); }
}
public static readonly DependencyProperty NodesProperty =
DependencyProperty.Register("Nodes", typeof(ObservableCollection<TreeNode>),
typeof(winTreeList), new PropertyMetadata(null, OnNodesChanged));
private static void OnNodesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (winTreeList)d;
control.treeView.ItemsSource = control.Nodes;
}
#endregion
#region
#region
2025-09-02 18:41:07 +08:00
public LabelTreeNode AddLabelNode(string text, TreeNode? parent = null)
2025-08-29 14:57:55 +08:00
{
2025-09-03 10:34:45 +08:00
var label = new Label()
{
2025-09-12 18:34:33 +08:00
Content = text,
Foreground = Brushes.White
2025-09-03 10:34:45 +08:00
};
var node = new LabelTreeNode
{
Text = text,
obj = label
};
2025-08-29 14:57:55 +08:00
AddNode(node, parent);
return node;
}
2025-09-03 10:34:45 +08:00
public CheckboxTreeNode AddCheckboxNode(string text, string? tag = null, bool isChecked = false, TreeNode? parent = null)
2025-08-29 14:57:55 +08:00
{
2025-09-03 10:34:45 +08:00
var checkbox = new CheckBox()
{
Visibility = Visibility.Visible,
2025-09-03 10:34:45 +08:00
IsChecked = isChecked,
Content = text,
Tag = tag ?? text,
ToolTip = tag ?? text,
2025-09-03 16:16:00 +08:00
Foreground = Brushes.White,
2025-09-03 10:34:45 +08:00
VerticalAlignment = VerticalAlignment.Center
};
var node = new CheckboxTreeNode
{
Text = text,
obj = checkbox
};
2025-08-29 14:57:55 +08:00
AddNode(node, parent);
return node;
}
2025-11-04 11:37:27 +08:00
public RadioButtonTreeNode AddRadioButtonNode(string text, string? tag = null,string? GroupName = null, bool isChecked = false, TreeNode? parent = null)
{
var RadioButton = new RadioButton()
{
Visibility = Visibility.Visible,
GroupName = GroupName,
IsChecked = isChecked,
Content = text,
Tag = tag ?? text,
ToolTip = tag ?? text,
Foreground = Brushes.White,
VerticalAlignment = VerticalAlignment.Center
};
var node = new RadioButtonTreeNode
{
Text = text,
obj = RadioButton
};
AddNode(node, parent);
return node;
}
2025-09-03 10:34:45 +08:00
public ComboboxTreeNode AddComboboxNode(string text, List<string>? items = null, TreeNode? parent = null)
2025-08-29 14:57:55 +08:00
{
2025-09-03 10:34:45 +08:00
var combobox = new ComboBox()
{
Visibility = Visibility.Visible,
2025-09-03 10:34:45 +08:00
SelectedIndex = 0,
VerticalAlignment = VerticalAlignment.Center,
MinWidth = 100
};
var node = new ComboboxTreeNode
{
2025-08-29 14:57:55 +08:00
Text = text,
2025-09-03 10:34:45 +08:00
obj = combobox
2025-08-29 14:57:55 +08:00
};
2025-09-03 10:34:45 +08:00
2025-08-29 14:57:55 +08:00
AddNode(node, parent);
return node;
}
2025-09-03 16:16:00 +08:00
#endregion
#region
public List<xTreeNode> FindAllTreeNodes<xTreeNode>(
IEnumerable<TreeNode>? nodes,
Func<TreeNode, bool> predicate = null)
where xTreeNode : class
2025-09-03 10:34:45 +08:00
{
2025-09-03 16:16:00 +08:00
var result = new List<xTreeNode>();
2025-09-03 10:34:45 +08:00
2025-09-03 16:16:00 +08:00
if (nodes == null)
nodes = Nodes;
2025-08-29 14:57:55 +08:00
foreach (var node in nodes)
{
2025-09-03 16:16:00 +08:00
// 检查当前节点是否匹配类型和条件
if (node is xTreeNode targetNode &&
(predicate == null || predicate(node)))
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
result.Add(targetNode);
2025-08-29 14:57:55 +08:00
}
// 递归查找子节点
2025-09-03 16:16:00 +08:00
if (node.Children.Count > 0)
{
var childResults = FindAllTreeNodes<xTreeNode>(node.Children, predicate);
result.AddRange(childResults);
}
2025-08-29 14:57:55 +08:00
}
2025-09-03 16:16:00 +08:00
return result;
2025-08-29 14:57:55 +08:00
}
2025-09-03 16:16:00 +08:00
#endregion
#region
2025-08-29 14:57:55 +08:00
/// <summary>
2025-09-03 16:16:00 +08:00
/// 从树中删除指定节点
2025-08-29 14:57:55 +08:00
/// </summary>
2025-09-03 16:16:00 +08:00
/// <param name="nodeToRemove">要删除的节点</param>
/// <returns>是否成功删除</returns>
public bool RemoveNode(TreeNode nodeToRemove)
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
if (nodeToRemove == null)
return false;
2025-08-29 14:57:55 +08:00
2025-09-03 16:16:00 +08:00
// 如果节点有父节点,从父节点的 Children 中删除
if (nodeToRemove.Parent != null)
{
return nodeToRemove.Parent.Children.Remove(nodeToRemove);
}
// 如果是根节点,从 Nodes 集合中删除
else
{
return Nodes.Remove(nodeToRemove);
2025-08-29 14:57:55 +08:00
}
}
2025-08-30 21:14:02 +08:00
/// <summary>
2025-09-03 16:16:00 +08:00
/// 批量删除多个节点
2025-08-30 21:14:02 +08:00
/// </summary>
2025-09-03 16:16:00 +08:00
/// <param name="nodesToRemove">要删除的节点列表</param>
public void RemoveNodes(IEnumerable<TreeNode> nodesToRemove)
{
foreach (var node in nodesToRemove.ToList()) // 使用 ToList() 避免修改集合时的枚举错误
2025-08-30 21:14:02 +08:00
{
2025-09-03 16:16:00 +08:00
RemoveNode(node);
2025-08-30 21:14:02 +08:00
}
}
2025-09-03 16:16:00 +08:00
#endregion
2025-08-30 21:14:02 +08:00
2025-09-03 16:16:00 +08:00
#region
public JObject JsonPrint(IEnumerable<TreeNode>? nodes = null, int loop = 0)
{
// 如果传入的节点为空,使用默认的 Nodes 集合
2025-08-29 14:57:55 +08:00
if (nodes == null) nodes = Nodes;
2025-09-03 16:16:00 +08:00
JObject resultObject = new JObject();
2025-08-29 14:57:55 +08:00
foreach (var node in nodes)
{
2025-09-03 16:16:00 +08:00
JObject nodeObject = new JObject();
// 为当前节点添加基本信息
nodeObject["Type"] = node.GetType().Name;
nodeObject["Text"] = node.Text; // 添加文本内容
// 使用模式匹配简化类型检查
switch (node)
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
case CheckboxTreeNode checkboxNode:
nodeObject["IsChecked"] = checkboxNode.obj.IsChecked?.ToString() ?? "false";
nodeObject["Tag"] = checkboxNode.obj.Tag?.ToString() ?? string.Empty;
break;
case ComboboxTreeNode comboboxNode:
nodeObject["SelectedItem"] = comboboxNode.obj.SelectedItem?.ToString() ?? string.Empty;
nodeObject["SelectedIndex"] = comboboxNode.obj.SelectedIndex;
// 添加所有选项
var items = new JArray();
foreach (var item in comboboxNode.obj.Items)
{
items.Add(item.ToString());
}
nodeObject["Items"] = items;
break;
case LabelTreeNode labelNode:
nodeObject["Content"] = labelNode.obj.Content?.ToString() ?? string.Empty;
break;
2025-08-29 14:57:55 +08:00
}
2025-09-03 16:16:00 +08:00
// 处理子节点 - 使用更合适的属性名
if (node.Children.Count > 0)
{
JObject childrenObject = JsonPrint(node.Children, loop + 1);
nodeObject["Children"] = childrenObject; // 改为更合适的属性名
}
2025-08-29 14:57:55 +08:00
2025-09-03 16:16:00 +08:00
// 使用唯一键名避免重复键问题
string key = node.Text;
int counter = 1;
while (resultObject.ContainsKey(key))
{
key = $"{node.Text}_{counter}";
counter++;
2025-08-30 21:14:02 +08:00
}
2025-08-29 14:57:55 +08:00
2025-09-03 16:16:00 +08:00
resultObject.Add(key, nodeObject);
2025-08-30 21:14:02 +08:00
}
2025-09-03 10:34:45 +08:00
2025-09-03 16:16:00 +08:00
return resultObject;
}
2025-09-03 10:34:45 +08:00
2025-09-03 16:16:00 +08:00
public void JsonParse(string jsonContent)
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
// 清空现有数据
Clear();
2025-08-30 21:14:02 +08:00
2025-09-03 16:16:00 +08:00
if (string.IsNullOrWhiteSpace(jsonContent))
return;
2025-08-30 21:14:02 +08:00
2025-09-03 16:16:00 +08:00
try
{
JObject rootObject = JObject.Parse(jsonContent);
ParseNodes(rootObject, null);
}
catch (Exception ex)
{
// 处理解析异常,可以根据需要记录日志或抛出异常
Debug.WriteLine($"JSON解析失败: {ex.Message}");
throw new ArgumentException("无效的JSON格式", ex);
}
2025-08-29 14:57:55 +08:00
}
2025-09-03 16:16:00 +08:00
private void ParseNodes(JObject jObject, TreeNode parent)
{
foreach (var property in jObject.Properties())
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
JObject nodeObject = property.Value as JObject;
if (nodeObject == null)
continue;
// 获取节点类型
string nodeType = nodeObject["Type"]?.ToString() ?? "";
string text = nodeObject["Text"]?.ToString() ?? "";
TreeNode newNode = null;
// 根据类型创建相应的节点
switch (nodeType)
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
case nameof(LabelTreeNode):
newNode = CreateLabelTreeNode(nodeObject, text);
2025-09-12 18:34:33 +08:00
2025-09-03 16:16:00 +08:00
break;
case nameof(CheckboxTreeNode):
newNode = CreateCheckboxTreeNode(nodeObject, text);
break;
case nameof(ComboboxTreeNode):
newNode = CreateComboboxTreeNode(nodeObject, text);
2025-09-12 18:34:33 +08:00
2025-09-03 16:16:00 +08:00
break;
default:
break;
2025-08-29 14:57:55 +08:00
}
2025-09-03 16:16:00 +08:00
if (newNode != null)
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
// 添加到树中
if (parent == null)
{
Nodes.Add(newNode);
}
else
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
newNode.Parent = parent;
parent.Children.Add(newNode);
2025-08-29 14:57:55 +08:00
}
2025-09-03 16:16:00 +08:00
// 递归处理子节点
if (nodeObject["Children"] is JObject childrenObject)
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
ParseNodes(childrenObject, newNode);
2025-08-29 14:57:55 +08:00
}
}
}
}
2025-09-03 16:16:00 +08:00
private LabelTreeNode CreateLabelTreeNode(JObject nodeObject, string text)
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
var labelNode = new LabelTreeNode
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
Text = text,
obj = new Label
2025-08-29 14:57:55 +08:00
{
2025-09-03 16:16:00 +08:00
Content = nodeObject["Content"]?.ToString() ?? text
2025-08-29 14:57:55 +08:00
}
2025-09-03 16:16:00 +08:00
};
return labelNode;
2025-08-29 14:57:55 +08:00
}
2025-09-03 16:16:00 +08:00
private CheckboxTreeNode CreateCheckboxTreeNode(JObject nodeObject, string text)
2025-09-02 18:41:07 +08:00
{
2025-09-03 16:16:00 +08:00
bool isChecked = bool.TryParse(nodeObject["IsChecked"]?.ToString(), out var result) && result;
string tag = nodeObject["Tag"]?.ToString() ?? text;
2025-09-02 18:41:07 +08:00
2025-09-03 16:16:00 +08:00
var checkboxNode = new CheckboxTreeNode
2025-09-02 18:41:07 +08:00
{
2025-09-03 16:16:00 +08:00
Text = text,
obj = new CheckBox
2025-09-02 18:41:07 +08:00
{
2025-09-03 16:16:00 +08:00
Content = text,
IsChecked = isChecked,
Tag = tag,
ToolTip = tag,
2025-09-12 18:34:33 +08:00
Foreground = Brushes.White,
2025-09-03 16:16:00 +08:00
VerticalAlignment = VerticalAlignment.Center
2025-09-02 18:41:07 +08:00
}
2025-09-03 16:16:00 +08:00
};
return checkboxNode;
}
2025-09-02 18:41:07 +08:00
2025-09-03 16:16:00 +08:00
private ComboboxTreeNode CreateComboboxTreeNode(JObject nodeObject, string text)
{
var comboboxNode = new ComboboxTreeNode
{
Text = text,
obj = new ComboBox
2025-09-02 18:41:07 +08:00
{
2025-09-03 16:16:00 +08:00
VerticalAlignment = VerticalAlignment.Center,
MinWidth = 100
2025-09-02 18:41:07 +08:00
}
2025-09-03 16:16:00 +08:00
};
2025-09-02 18:41:07 +08:00
2025-09-03 16:16:00 +08:00
// 添加选项
if (nodeObject["Items"] is JArray itemsArray)
{
foreach (var item in itemsArray)
2025-09-02 18:41:07 +08:00
{
2025-09-03 16:16:00 +08:00
comboboxNode.obj.Items.Add(item.ToString());
2025-09-02 18:41:07 +08:00
}
2025-09-03 16:16:00 +08:00
}
2025-09-02 18:41:07 +08:00
2025-09-03 16:16:00 +08:00
// 设置选中项
int selectedIndex = nodeObject["SelectedIndex"]?.Value<int>() ?? 0;
if (selectedIndex >= 0 && selectedIndex < comboboxNode.obj.Items.Count)
{
comboboxNode.obj.SelectedIndex = selectedIndex;
2025-09-02 18:41:07 +08:00
}
2025-09-03 16:16:00 +08:00
return comboboxNode;
2025-09-02 18:41:07 +08:00
}
2025-08-29 14:57:55 +08:00
#endregion
#region
public void Clear()
{
Nodes.Clear();
}
2025-09-12 18:34:33 +08:00
public void ExpandAll(object? sender = null, RoutedEventArgs? e = null)
2025-08-29 14:57:55 +08:00
{
foreach (var node in Nodes)
{
ExpandNode(node);
}
}
2025-09-12 18:34:33 +08:00
public void CollapseAll(object? sender=null, RoutedEventArgs? e=null)
2025-08-29 14:57:55 +08:00
{
foreach (var node in Nodes)
{
CollapseNode(node);
}
}
2025-09-03 16:16:00 +08:00
public void ExpandNode(TreeNode node)
2025-08-29 14:57:55 +08:00
{
var container = treeView.ItemContainerGenerator.ContainerFromItem(node) as TreeViewItem;
if (container != null)
{
container.IsExpanded = true;
}
foreach (var child in node.Children)
{
ExpandNode(child);
}
}
2025-09-03 16:16:00 +08:00
public void CollapseNode(TreeNode node)
2025-08-29 14:57:55 +08:00
{
var container = treeView.ItemContainerGenerator.ContainerFromItem(node) as TreeViewItem;
if (container != null)
{
container.IsExpanded = false;
}
foreach (var child in node.Children)
{
CollapseNode(child);
}
}
2025-09-03 16:16:00 +08:00
#endregion
#endregion
2025-08-29 14:57:55 +08:00
2025-09-03 16:16:00 +08:00
#region
2025-09-12 18:34:33 +08:00
public void AddNode(TreeNode node, TreeNode parent)
2025-09-03 16:16:00 +08:00
{
if (parent == null)
{
node.Parent = null;
Nodes.Add(node);
}
else
{
node.Parent = parent;
parent.Children.Add(node);
}
}
private IEnumerable<TreeNode> GetAllNodes(IEnumerable<TreeNode>? nodes=null)
{
if (nodes == null) nodes = null;
foreach (var node in nodes)
{
yield return node;
foreach (var child in GetAllNodes(node.Children))
{
yield return child;
}
}
}
2025-08-29 14:57:55 +08:00
#endregion
}
#region
2025-09-03 10:34:45 +08:00
public abstract class TreeNode : INotifyPropertyChanged
2025-08-29 14:57:55 +08:00
{
2025-09-03 10:34:45 +08:00
private string _text;
private ObservableCollection<TreeNode> _children;
2025-09-03 16:16:00 +08:00
private TreeNode _parent;
2025-09-03 10:34:45 +08:00
2025-09-03 16:16:00 +08:00
#region
2025-09-03 10:34:45 +08:00
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
2025-08-29 14:57:55 +08:00
public TreeNode()
{
2025-09-03 10:34:45 +08:00
_children = new ObservableCollection<TreeNode>();
}
2025-09-03 16:16:00 +08:00
#endregion
2025-09-03 10:34:45 +08:00
public string Text
{
get => _text;
set
{
if (_text != value)
{
_text = value;
OnPropertyChanged(nameof(Text));
}
}
2025-08-29 14:57:55 +08:00
}
2025-09-03 10:34:45 +08:00
public ObservableCollection<TreeNode> Children
{
get => _children;
set
{
if (_children != value)
{
_children = value;
OnPropertyChanged(nameof(Children));
}
}
}
2025-08-29 14:57:55 +08:00
2025-09-03 16:16:00 +08:00
public TreeNode Parent
{
get => _parent;
set
{
if (_parent != value)
{
// 防止设置自己为自己的父节点
if (value == this)
throw new InvalidOperationException("不能将节点设置为自己的父节点");
// 防止循环引用(检查祖先链)
var current = value;
while (current != null)
{
if (current == this)
throw new InvalidOperationException("检测到循环引用");
current = current.Parent;
}
_parent = value;
OnPropertyChanged(nameof(Parent));
}
}
}
}
2025-08-29 14:57:55 +08:00
public class LabelTreeNode : TreeNode
{
2025-09-03 10:34:45 +08:00
private Label _obj;
public Label obj
{
get => _obj;
set
{
if (_obj != value)
{
_obj = value;
OnPropertyChanged(nameof(obj));
}
}
}
2025-08-29 14:57:55 +08:00
}
public class ComboboxTreeNode : TreeNode
{
2025-09-03 10:34:45 +08:00
private ComboBox _obj;
public ComboBox obj
{
get => _obj;
set
{
if (_obj != value)
{
_obj = value;
OnPropertyChanged(nameof(obj));
}
}
}
2025-08-29 14:57:55 +08:00
}
public class CheckboxTreeNode : TreeNode
{
2025-09-03 10:34:45 +08:00
private CheckBox _obj;
public CheckBox obj
2025-08-29 14:57:55 +08:00
{
2025-09-03 10:34:45 +08:00
get => _obj;
2025-08-29 14:57:55 +08:00
set
{
2025-09-03 10:34:45 +08:00
if (_obj != value)
2025-08-29 14:57:55 +08:00
{
2025-09-03 10:34:45 +08:00
_obj = value;
OnPropertyChanged(nameof(obj));
2025-08-29 14:57:55 +08:00
}
}
}
}
2025-11-04 11:37:27 +08:00
public class RadioButtonTreeNode : TreeNode
{
private RadioButton _obj;
public RadioButton obj
{
get => _obj;
set
{
if (_obj != value)
{
_obj = value;
OnPropertyChanged(nameof(obj));
}
}
}
}
2025-08-29 14:57:55 +08:00
#endregion
}