Node.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. using PlcUiControl;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.ComponentModel.Design.Serialization;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Windows.Forms;
  9. [TypeConverter(typeof(NodeConverter))]
  10. [Serializable]
  11. public class Node
  12. {
  13. private string _value;
  14. public static readonly string NodeFilePath = @"D:\XmlParser\ReadNodes.txt";
  15. public static List<string> AllNodes=new List<string>();
  16. public static bool EnableDesignTimeValidation { get; set; } = true; // 默认开启,提醒开发者
  17. public string Value
  18. {
  19. get => _value;
  20. set
  21. {
  22. value=value.Trim();
  23. if (string.IsNullOrWhiteSpace(value))
  24. {
  25. throw new ArgumentException("地址不能为空");
  26. }
  27. if (EnableDesignTimeValidation && !IsValidNode(value))
  28. {
  29. throw new ArgumentException($"地址\"{value}\"无效,请输入正确的地址,或导入XML后选择对应的地址。");
  30. //var dr = MessageBox.Show($"地址\"{value}\"无效,是否坚持绑定此地址?", "询问", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
  31. //if (dr == DialogResult.No)
  32. //{
  33. // return;
  34. //}
  35. }
  36. _value = value;
  37. }
  38. }
  39. public string Name { get; set; }
  40. public NodeType Type { get; set; }
  41. public string Comment { get; set; }
  42. public Node()
  43. {
  44. }
  45. public Node(string value)
  46. {
  47. _value = value;
  48. Name = null;
  49. Type = NodeType.Unspecified;
  50. Comment = null;
  51. }
  52. public Node(string value, NodeType nodeType)
  53. {
  54. _value = value;
  55. //Name = StringExtensions.AfterLast(value, '.');
  56. Type = nodeType;
  57. Comment = null;
  58. }
  59. public Node(string value,NodeType nodeType,string comment)
  60. {
  61. _value = value;
  62. Name = StringExtensions.AfterLast(value, '.');
  63. Type = nodeType;
  64. Comment = comment;
  65. }
  66. public override string ToString()
  67. {
  68. return _value;
  69. }
  70. private bool IsValidNode(string input)
  71. {
  72. try
  73. {
  74. if (AllNodes.Contains(input))//先在内存中查找
  75. {
  76. return true;
  77. }
  78. if (!File.Exists(NodeFilePath))
  79. {
  80. throw new FileNotFoundException("ReadNodes.txt 文件未找到", NodeFilePath);
  81. }
  82. // 读取文件所有行并比较(忽略前后空格)
  83. return File.ReadLines(NodeFilePath)
  84. .Any(line => line.Trim() == input.Trim());
  85. }
  86. catch (Exception ex)
  87. {
  88. // 可以记录日志或抛出异常
  89. Console.WriteLine("验证地址时出错:" + ex.Message);
  90. return false;
  91. }
  92. }
  93. }
  94. public class NodeConverter : TypeConverter
  95. {
  96. // 1. 允许展开属性
  97. public override bool GetPropertiesSupported(ITypeDescriptorContext context)
  98. {
  99. return true; // 关键:返回 true 才能展开
  100. }
  101. // 2. 返回子属性集合
  102. public override PropertyDescriptorCollection GetProperties(
  103. ITypeDescriptorContext context,
  104. object value,
  105. Attribute[] attributes)
  106. {
  107. // 获取 Node 类的所有公共属性
  108. return TypeDescriptor.GetProperties(value, attributes)
  109. .Sort(new string[] { "Name", "Type", "Comment", "Value" }); // 可选排序
  110. }
  111. public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  112. {
  113. // 支持从字符串转换
  114. if (sourceType == typeof(string))
  115. {
  116. return true;
  117. }
  118. return base.CanConvertFrom(context, sourceType);
  119. }
  120. public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  121. {
  122. // 当用户输入字符串时,尝试构造 ReadNode 对象
  123. if (value is string str)
  124. {
  125. str= str.Trim();
  126. if (!string.IsNullOrEmpty(str))
  127. return new Node { Value = str };
  128. else
  129. return null;
  130. }
  131. return base.ConvertFrom(context, culture, value);
  132. }
  133. public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
  134. {
  135. if (destinationType == typeof(InstanceDescriptor) && value is Node n)
  136. {
  137. var ctor1 = typeof(Node).GetConstructor(new[] { typeof(string), typeof(NodeType), typeof(string) });
  138. if (ctor1 != null)
  139. {
  140. return new InstanceDescriptor(ctor1, new object[] { n.Value, n.Type, n.Comment });
  141. }
  142. else
  143. {
  144. MessageBox.Show("ConvertTo ctor1 Failed");
  145. }
  146. // 回退到单参数构造函数(更安全)
  147. var ctor2 = typeof(Node).GetConstructor(new[] { typeof(string), typeof(NodeType) });
  148. if (ctor2 != null)
  149. {
  150. return new InstanceDescriptor(ctor2, new object[] { n.Value, n.Type });
  151. }
  152. else
  153. {
  154. MessageBox.Show("ConvertTo ctor2 Failed");
  155. }
  156. }
  157. return base.ConvertTo(context, culture, value, destinationType);
  158. }
  159. public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
  160. {
  161. // 明确告诉设计器:可以生产new Node(...) 代码
  162. if (destinationType == typeof(InstanceDescriptor))
  163. {
  164. return true; // ✅ 关键!允许代码生成
  165. }
  166. if (destinationType == typeof(string))//如果返回True,那么设计器会调用ConvertTo 进行字符串格式化。
  167. {
  168. return false; // ✅ 关键!
  169. }
  170. return base.CanConvertTo(context, destinationType);
  171. }
  172. }
  173. public enum NodeType
  174. {
  175. BOOL=0,
  176. BYTE,
  177. WORD,
  178. DWORD,
  179. LWORD,
  180. SINT,
  181. USINT,
  182. INT,
  183. UINT,
  184. DINT,
  185. UDINT,
  186. LINT,
  187. ULINT,
  188. REAL,
  189. LREAL,
  190. TIME,//UINT32
  191. STRING,
  192. Unspecified = -1,
  193. }