using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml.Linq; using System.Xml.Serialization; namespace XmlToTreeView { public partial class NodeSelectionDialog : Form { private Node selectedValue; public Node SelectedValue { get => selectedValue; set => selectedValue = value; } static readonly string ConfigPath = @"D:\XmlParser\Config.txt"; static readonly string TreeViewState = @"D:\XmlParser\treeview_state.xml"; public NodeSelectionDialog() { InitializeComponent(); } private void NodeSelectionDialog_Load(object sender, EventArgs e) { this.Size = new System.Drawing.Size(1024, 768); if (File.Exists(ConfigPath)) { var xmlPath = File.ReadAllText(ConfigPath); LoadTree(xmlPath,false); } LoadTreeViewState(treeView); txtSearch.Focus(); } private void LoadTree(string xmlPath,bool isUserLoad) { if (!File.Exists(xmlPath)) { return; } try { var types = SymbolParser.ParseTypeList(xmlPath); var root = SymbolParser.ParseNode(xmlPath, types); treeView.Nodes.Clear(); AddToTreeView(treeView.Nodes, root); foreach (TreeNode node in treeView.Nodes) { node.Expand(); } if (isUserLoad)//只有用户打开的时候才去保存 { FileInfo fileInfo = new FileInfo(ConfigPath); DirectoryInfo dir = fileInfo.Directory; Directory.CreateDirectory(dir.FullName); File.WriteAllText(ConfigPath, xmlPath); Node.AllNodes = new List(); GenerateFullPaths(root, Node.AllNodes); File.WriteAllLines(Node.NodeFilePath, Node.AllNodes.ToArray()); } } catch (Exception ex) { MessageBox.Show(ex.Message,"错误",MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } private void AddToTreeView(TreeNodeCollection nodes, XmlVariable variableNode) { TreeNode tn = new TreeNode(variableNode.DisplayName); tn.Tag = variableNode.Name; foreach (var child in variableNode.Children) { AddToTreeView(tn.Nodes, child); } nodes.Add(tn); } private string GetFullPath(TreeNode node) { List pathParts = new List(); TreeNode currentNode = node; while (currentNode != null) { // 获取当前节点显示的文本 pathParts.Add(currentNode.Tag.ToString()); // 获取父节点 currentNode = currentNode.Parent; } pathParts.Reverse(); // 从根到叶子排列 return string.Join(".", pathParts); } private void GenerateFullPaths(XmlVariable root, List paths, string currentPath = "") { string newPath = string.IsNullOrEmpty(currentPath) ? root.Name : $"{currentPath}.{root.Name}"; if (root.IsLeaf) { paths.Add(ProcessArrayPath(newPath)); } else { foreach (var child in root.Children) { GenerateFullPaths(child, paths, newPath); } } } string ProcessArrayPath(string path) { // 第一步:去掉 .[ 前面的点,变成 [ string result = path; while (true) { int index = result.IndexOf(".[", StringComparison.Ordinal); if (index == -1) break; result = result.Substring(0, index) + "[" + result.Substring(index + 2); } // 第二步:合并 [1][2] → [1,2] while (true) { int index = result.IndexOf("][", StringComparison.Ordinal); if (index == -1) break; // 替换 ][ 为 ,,并删除多余的 ] result = result.Substring(0, index) + "," + result.Substring(index + 2); } return result; } private void 打开XMLOToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "XML文档|*.xml"; openFileDialog.Multiselect = false; openFileDialog.RestoreDirectory = true; if (openFileDialog.ShowDialog() == DialogResult.OK) { LoadTree(openFileDialog.FileName,true); } } private void treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { if (e.Node.Nodes.Count > 0) return; var currentNode = treeView.GetNodeAt(e.X, e.Y); if (currentNode != null) { string fullPath = GetFullPath(currentNode); txtNode.Text = ProcessArrayPath(fullPath); txtType.Text = GetTypeString(currentNode.Text,'(',')'); txtComment.Text = GetTypeString(currentNode.Text, '<', '>'); } } string GetTypeString(string nodeTxt,char spliter1, char spliter2) { int startIndex = nodeTxt.LastIndexOf(spliter1); int endIndex = nodeTxt.LastIndexOf(spliter2); if (startIndex >= 0 && endIndex > startIndex) { string result = nodeTxt.Substring(startIndex + 1, endIndex - startIndex - 1); return result; } return null; } bool userSelectedNode; private void treeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) { if (e.Node.Nodes.Count > 0) return; var currentNode = treeView.GetNodeAt(e.X, e.Y); if (currentNode != null) { string fullPath = GetFullPath(currentNode); string rt = ProcessArrayPath(fullPath); txtNode.Text = rt; var typeString= GetTypeString(currentNode.Text, '(', ')'); bool isDefinedType = Enum.TryParse(typeString, out NodeType type) && Enum.IsDefined(typeof(NodeType), type); if (isDefinedType) { SelectedValue = new Node(rt, type, GetTypeString(currentNode.Text, '<', '>')); DialogResult = DialogResult.OK; userSelectedNode = true; Close(); } else { MessageBox.Show($"暂不支持:{typeString}类型数据","错误",MessageBoxButtons.OK,MessageBoxIcon.Error); return; } } } private void LoadTreeViewState(System.Windows.Forms.TreeView treeView) { if (!File.Exists(TreeViewState)) return; using (FileStream fs = new FileStream(TreeViewState, FileMode.Open)) { XmlSerializer serializer = new XmlSerializer(typeof(TreeViewState)); var state = (TreeViewState)serializer.Deserialize(fs); // 恢复展开状态 foreach (var node in GetAllNodes(treeView.Nodes)) { if (state.ExpandedNodeKeys.Contains(GetNodeKey(node))) { node.Expand(); } } // 恢复选中状态 if (!string.IsNullOrEmpty(state.SelectedNodeKey)) { TreeNode selectedNode = FindNodeByPath(treeView, state.SelectedNodeKey); if (selectedNode != null) { treeView.SelectedNode = selectedNode; string fullPath = GetFullPath(selectedNode); txtNode.Text = ProcessArrayPath(fullPath); } } } } // 根据 FullPath 查找节点 private TreeNode FindNodeByPath(System.Windows.Forms.TreeView treeView, string fullPath) { foreach (var node in GetAllNodes(treeView.Nodes)) { if (((TreeNode)node).FullPath == fullPath) { return (TreeNode)node; } } return null; } private void NodeSelectionDialog_FormClosed(object sender, FormClosedEventArgs e) { if(userSelectedNode) SaveTreeViewState(treeView); } private void SaveTreeViewState(System.Windows.Forms.TreeView treeView) { TreeViewState state = new TreeViewState(); // 保存选中节点 if (treeView.SelectedNode != null) { state.SelectedNodeKey = GetNodeKey(treeView.SelectedNode); } // 保存所有已展开的节点 foreach (TreeNode node in GetAllNodes(treeView.Nodes)) { if (node.IsExpanded) { state.ExpandedNodeKeys.Add(GetNodeKey(node)); } } // 使用序列化保存到文件或其他地方 using (FileStream fs = new FileStream(TreeViewState, FileMode.Create)) { XmlSerializer serializer = new XmlSerializer(typeof(TreeViewState)); serializer.Serialize(fs, state); } } // 获取唯一标识符(可以是 FullPath、Text 或你自己设计的 Key) private string GetNodeKey(TreeNode node) { return node.FullPath; } // 遍历所有节点(递归) private IEnumerable GetAllNodes(TreeNodeCollection nodes) { foreach (TreeNode node in nodes) { yield return node; foreach (var child in GetAllNodes(node.Nodes)) { yield return child; } } } private void btnSearch_Click(object sender, EventArgs e) { string searchText = txtSearch.Text.Trim(); if (string.IsNullOrEmpty(searchText)) return; treeView.SelectedNode = null; _firstMatchNode = null; matchCount = 0; CollapseAllNodes(treeView); FindAndExpandNodes(treeView,txtSearch.Text); // 如果有第一个匹配项,手动滚动到顶部 if (_firstMatchNode != null && matchCount>1) { // 先确保节点可见(再次调用,避免 EnsureVisible 未生效) _firstMatchNode.EnsureVisible(); // 使用反射或 API 设置滚动条位置到顶部 ScrollTreeViewToTopLeft(treeView, _firstMatchNode); } } private TreeNode _firstMatchNode = null; int matchCount = 0; private void FindAndExpandNodes(System.Windows.Forms.TreeView treeView, string searchText) { if (string.IsNullOrEmpty(searchText)) return; foreach (var item in foundedNodes) { item.BackColor = Color.Transparent; } foundedNodes.Clear(); foreach (TreeNode node in treeView.Nodes) { SearchNodeRecursively(node, searchText); } } List foundedNodes=new List (); private bool SearchNodeRecursively(TreeNode node, string searchText) { bool found = false; // 先检查当前节点是否匹配 if (node.Text.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0) { node.Expand(); // 展开当前节点 node.EnsureVisible(); // 确保节点可见 if (treeView.SelectedNode == null) { treeView.SelectedNode = node; // 选中第一个匹配的节点 } Color color = node.BackColor; node.BackColor = Color.LimeGreen; //var timer = new Timer() { Tag = node, Interval = 10000 }; //timer.Tick += (object s, EventArgs e) => { ((TreeNode)((Timer)s).Tag).BackColor = Color.Transparent; ((Timer)s).BackgroundRefreshEnable = false; }; //timer.Start(); foundedNodes.Add(node); if (_firstMatchNode == null) { _firstMatchNode = node; // 记录第一个匹配的节点 } matchCount++; found = true; } // 再检查子节点 foreach (TreeNode child in node.Nodes) { if (SearchNodeRecursively(child, searchText)) { matchCount++; found = true; node.Expand(); // 如果子节点匹配,父节点也要展开 } } return found; } private void CollapseAllNodes(System.Windows.Forms.TreeView treeView) { foreach (TreeNode node in treeView.Nodes) { CollapseNodeRecursively(node); } } private void CollapseNodeRecursively(TreeNode node) { if (node.IsExpanded) { node.Collapse(); // 折叠当前节点 } foreach (TreeNode child in node.Nodes) { CollapseNodeRecursively(child); // 递归处理子节点 } } [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam); private const int WM_VSCROLL = 0x0115; private const int WM_HSCROLL = 0x0114; private const int SB_TOP = 6; private const int SB_LEFT = 6; private void ScrollTreeViewToTopLeft(System.Windows.Forms.TreeView treeView, TreeNode node) { // 确保节点可见(垂直方向) node.EnsureVisible(); // 垂直滚动到顶部 SendMessage(treeView.Handle, WM_VSCROLL, (IntPtr)SB_TOP, IntPtr.Zero); // 水平滚动到最左边 SendMessage(treeView.Handle, WM_HSCROLL, (IntPtr)SB_LEFT, IntPtr.Zero); } private void txtNode_DoubleClick(object sender, EventArgs e) { txtNode.SelectAll(); } private void txtSearch_Click(object sender, EventArgs e) { txtSearch.SelectAll(); } } [Serializable] public class TreeViewState { public string SelectedNodeKey { get; set; } public List ExpandedNodeKeys { get; set; } = new List(); public List AllNodes { get; set; } = new List(); } }