using PlcUiControl; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.IO; using System.Linq; using System.Windows.Forms; [TypeConverter(typeof(NodeConverter))] [Serializable] public class Node { private string _value; public static readonly string NodeFilePath = @"D:\XmlParser\ReadNodes.txt"; public static List AllNodes=new List(); public static bool EnableDesignTimeValidation { get; set; } = true; // 默认开启,提醒开发者 public string Value { get => _value; set { value=value.Trim(); if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("地址不能为空"); } if (EnableDesignTimeValidation && !IsValidNode(value)) { throw new ArgumentException($"地址\"{value}\"无效,请输入正确的地址,或导入XML后选择对应的地址。"); //var dr = MessageBox.Show($"地址\"{value}\"无效,是否坚持绑定此地址?", "询问", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); //if (dr == DialogResult.No) //{ // return; //} } _value = value; } } public string Name { get; set; } public NodeType Type { get; set; } public string Comment { get; set; } public Node() { } public Node(string value) { _value = value; Name = null; Type = NodeType.Unspecified; Comment = null; } public Node(string value, NodeType nodeType) { _value = value; //Name = StringExtensions.AfterLast(value, '.'); Type = nodeType; Comment = null; } public Node(string value,NodeType nodeType,string comment) { _value = value; Name = StringExtensions.AfterLast(value, '.'); Type = nodeType; Comment = comment; } public override string ToString() { return _value; } private bool IsValidNode(string input) { try { if (AllNodes.Contains(input))//先在内存中查找 { return true; } if (!File.Exists(NodeFilePath)) { throw new FileNotFoundException("ReadNodes.txt 文件未找到", NodeFilePath); } // 读取文件所有行并比较(忽略前后空格) return File.ReadLines(NodeFilePath) .Any(line => line.Trim() == input.Trim()); } catch (Exception ex) { // 可以记录日志或抛出异常 Console.WriteLine("验证地址时出错:" + ex.Message); return false; } } } public class NodeConverter : TypeConverter { // 1. 允许展开属性 public override bool GetPropertiesSupported(ITypeDescriptorContext context) { return true; // 关键:返回 true 才能展开 } // 2. 返回子属性集合 public override PropertyDescriptorCollection GetProperties( ITypeDescriptorContext context, object value, Attribute[] attributes) { // 获取 Node 类的所有公共属性 return TypeDescriptor.GetProperties(value, attributes) .Sort(new string[] { "Name", "Type", "Comment", "Value" }); // 可选排序 } public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { // 支持从字符串转换 if (sourceType == typeof(string)) { return true; } return base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { // 当用户输入字符串时,尝试构造 ReadNode 对象 if (value is string str) { str= str.Trim(); if (!string.IsNullOrEmpty(str)) return new Node { Value = str }; else return null; } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(InstanceDescriptor) && value is Node n) { var ctor1 = typeof(Node).GetConstructor(new[] { typeof(string), typeof(NodeType), typeof(string) }); if (ctor1 != null) { return new InstanceDescriptor(ctor1, new object[] { n.Value, n.Type, n.Comment }); } else { MessageBox.Show("ConvertTo ctor1 Failed"); } // 回退到单参数构造函数(更安全) var ctor2 = typeof(Node).GetConstructor(new[] { typeof(string), typeof(NodeType) }); if (ctor2 != null) { return new InstanceDescriptor(ctor2, new object[] { n.Value, n.Type }); } else { MessageBox.Show("ConvertTo ctor2 Failed"); } } return base.ConvertTo(context, culture, value, destinationType); } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { // 明确告诉设计器:可以生产new Node(...) 代码 if (destinationType == typeof(InstanceDescriptor)) { return true; // ✅ 关键!允许代码生成 } if (destinationType == typeof(string))//如果返回True,那么设计器会调用ConvertTo 进行字符串格式化。 { return false; // ✅ 关键! } return base.CanConvertTo(context, destinationType); } } public enum NodeType { BOOL=0, BYTE, WORD, DWORD, LWORD, SINT, USINT, INT, UINT, DINT, UDINT, LINT, ULINT, REAL, LREAL, TIME,//UINT32 STRING, Unspecified = -1, }