| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- 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<string>();
- 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<string> pathParts = new List<string>();
- 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<string> 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<NodeType>(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<TreeNode> 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<TreeNode> foundedNodes=new List<TreeNode> ();
- 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<string> ExpandedNodeKeys { get; set; } = new List<string>();
- public List<string> AllNodes { get; set; } = new List<string>();
- }
- }
|