• C#完成XML文档节点的自动计算功能


      一个项目涉及XML文档中节点的自动计算,就是XML文档的每个节点都参与运算,要求:

      ⑴如果节点有计算公式则按照计算公式进行;

      ⑵如果节点没有计算公式则该节点的值就是所有子节点的值之和;

      ⑶节点有4种类型,计算节点、输入框、单选节点、多选节点;

        计算节点:汇总;

        输入框:点击该节点弹出输入框用于输入数据;

        单选节点:众多选项中只能选择一个,根据选择项确定该节点的具体值;

        多选节点:众多选项中可以选择多个,该节点的值是所有选择项的和;

      类似下图(实际选项近100个):

      问题是点击任何图标节点后都要完成的自动计算。

      开始的时候,我将所有XML信息加载到Treeview中,包括属性,在Treeview中进行计算,完成后同步到XML文档,这样完成后效果不好,选项多了速度慢,如果计算机配置一般的话有略微的卡顿。

      今天下午,我修改了方法,直接在XML文档中进行操作,使用递归完成节点的自动计算,这样速度很快,并且不需要同步到Treeview中(因为Treeview只是用于显示)。

      1、点击节点

      在Treeview中确定节点,根据节点类型完成图标变化,在XML中找到对应的节点。

      ⑴完成状态标识,如果是Radio则标识单选;如果是Checkbox标识多选;

      ⑵提取Value值,如果是Textbox则是输入值,如果是Radio则是父项value值是点击节点的Value值;如果是Checkbox则父项是所有选择项的value值之和。

      ⑶调用自动计算,如果是Radio或者Checkbox则是从父项的父项开始,如果是Textbox则是从父项开始。

    1. private void treeView1_MouseDown(object sender, MouseEventArgs e)
    2. {
    3. //获取鼠标点击的位置
    4. TreeNode FocusNode = treeView1.GetNodeAt(e.Location);
    5. string StrCurrentFullPath = FocusNode.FullPath;
    6. string StrNodeType = "";
    7. if (FocusNode != null)
    8. {
    9. //获取鼠标点击的位置是否在节点的图标上
    10. //在Treeview中针对Radio、Checkbox、TextBook分别进行设置
    11. TreeViewHitTestInfo hitTestInfo = treeView1.HitTest(e.Location);
    12. if (hitTestInfo.Location == TreeViewHitTestLocations.Image)
    13. {
    14. StrNodeType = FocusNode.Tag.ToString();
    15. //鼠标点击了节点的图标
    16. switch (StrNodeType)
    17. {
    18. case "Radio":
    19. // 取消同级节点的选中状态
    20. foreach (TreeNode node1 in FocusNode.Parent.Nodes)
    21. {
    22. if (node1 != FocusNode)
    23. {
    24. node1.ImageKey = "Radio";
    25. node1.SelectedImageKey = "Radio";
    26. }
    27. }
    28. // 设置当前节点为选中状态
    29. FocusNode.ImageKey = "RadioChecked";
    30. FocusNode.SelectedImageKey = "RadioChecked";
    31. //在XML文档中处理
    32. HandleNodeInfoAtXmlContent(StrCurrentFullPath, StrNodeType,"");//在文档中找到该节点并处理
    33. //
    34. break;
    35. case "Checkbox":
    36. if (FocusNode.ImageKey == "Checkbox")
    37. {
    38. FocusNode.ImageKey = "CheckboxChecked";
    39. FocusNode.SelectedImageKey = "CheckboxChecked";
    40. }
    41. else
    42. {
    43. FocusNode.ImageKey = "Checkbox";
    44. FocusNode.SelectedImageKey = "Checkbox";
    45. }
    46. //在XML文档中处理
    47. HandleNodeInfoAtXmlContent(StrCurrentFullPath, StrNodeType,"");//在文档中找到该节点并处理
    48. break;
    49. case "Textbox":
    50. string StrMin = "";
    51. string StrMax = "";
    52. string StrMemo = "";
    53. float fTemp;
    54. ToTextboxInputWinPara.fMax = 0;
    55. ToTextboxInputWinPara.fMin = 0;
    56. ToTextboxInputWinPara.StrMemo = "";
    57. FrmTextBoxInput FTI= new FrmTextBoxInput();
    58. DialogResult result= FTI.ShowDialog();
    59. if(result == DialogResult.OK)
    60. {
    61. StrCurrentTextboxValue = FTI.StrReturn;
    62. }
    63. //在XML文档中处理
    64. HandleNodeInfoAtXmlContent(StrCurrentFullPath, StrNodeType, StrCurrentTextboxValue);//在文档中找到该节点并处理
    65. break;
    66. }
    67. treeView1.Invalidate();
    68. }
    69. if (hitTestInfo.Location == TreeViewHitTestLocations.Label)
    70. {
    71. //点击标签
    72. if (FocusNode.Tag != null)
    73. {
    74. switch (FocusNode.Tag.ToString())
    75. {
    76. case "Radio":
    77. if (FocusNode.ImageKey == "RadioChecked")
    78. {
    79. FocusNode.SelectedImageKey = "RadioChecked";
    80. }
    81. if (FocusNode.ImageKey == "Radio")
    82. {
    83. FocusNode.SelectedImageKey = "Radio";
    84. }
    85. break;
    86. case "Checkbox":
    87. if (FocusNode.ImageKey == "Checkbox")
    88. {
    89. FocusNode.SelectedImageKey = "Checkbox";
    90. }
    91. if (FocusNode.ImageKey == "CheckboxChecked")
    92. {
    93. FocusNode.SelectedImageKey = "CheckboxChecked";
    94. }
    95. break;
    96. default: break;
    97. }
    98. treeView1.Invalidate();
    99. }
    100. }
    101. }
    102. }

      对应在XML文档中的处理函数:

    1. private void HandleNodeInfoAtXmlContent(string StrCurrentFullPath,string StrNodeType,string StrInputTextValue)
    2. {
    3. //在XML文档内容中处理节点信息,传入参数:StrCurrentFullPath是当前点击选择的节点全路径名称
    4. int FirstIndex = StrCurrentFullPath.IndexOf("\\");
    5. int LastIndex = StrCurrentFullPath.LastIndexOf("\\");
    6. string StrCurrentNodeName = StrCurrentFullPath.Substring(LastIndex + 1);
    7. //提取父节点的名称
    8. string[] SubStr= StrCurrentFullPath.Split("\\");
    9. string ParentStr = SubStr[SubStr.Length - 2];
    10. // 使用XPath表达式定位到具体的节点,点击的节点名称是caption值
    11. string XpathExpression="";
    12. XmlNode CalculateNode=null;//计算节点
    13. switch (StrNodeType)
    14. {
    15. case "Radio":
    16. XpathExpression = "//" + ParentStr + "/option[@caption='" + StrCurrentNodeName + "']";
    17. break;
    18. case "Checkbox":
    19. XpathExpression = "//" + ParentStr + "/input[@caption='" + StrCurrentNodeName + "']";
    20. break;
    21. case "Textbox":
    22. XpathExpression = "//" + ParentStr + "/"+ StrCurrentNodeName;
    23. break;
    24. }
    25. XmlNode BeSelectNode = XmlDoc.SelectSingleNode(XpathExpression);
    26. //得到父节点的全路径名
    27. string SParentPath = StrCurrentFullPath.Substring(0, LastIndex);
    28. //得到父节点
    29. XmlNode ParentNode = FindNodeAtXmlContentByFullPath(SParentPath);
    30. XmlNode TempNode = null;
    31. if (BeSelectNode != null && ParentNode!=null)
    32. {
    33. //根据节点类型处理本节点
    34. switch (StrNodeType)
    35. {
    36. case "Radio":
    37. string StrValue = "";
    38. //找到该节点标识选中状态
    39. foreach (XmlNode RadioChildNode in ParentNode.ChildNodes)
    40. {
    41. //单选,先将父节点下的子节点的select属性全部删除
    42. if (RadioChildNode.Attributes["select"] != null)
    43. {
    44. RadioChildNode.Attributes.Remove(RadioChildNode.Attributes["select"]);
    45. }
    46. //找到子节点
    47. if (RadioChildNode.Attributes["caption"].Value == StrCurrentNodeName)
    48. {
    49. TempNode = RadioChildNode;
    50. StrValue = TempNode.Attributes["value"].Value;
    51. }
    52. }
    53. //添加select属性
    54. if (TempNode!=null)
    55. {
    56. if (HasAttribute(TempNode, "select"))
    57. {
    58. TempNode.Attributes["select"].Value = "true";
    59. }
    60. else
    61. {
    62. XmlAttribute RadioNodeAttr = XmlDoc.CreateAttribute("select");
    63. RadioNodeAttr.Value = "true";
    64. TempNode.Attributes.Append(RadioNodeAttr);
    65. }
    66. }
    67. //为父节点的value属性赋值
    68. ParentNode.Attributes["value"].Value = StrValue;
    69. //寻找父节点的父节点
    70. CalculateNode = ParentNode.ParentNode;
    71. //计算
    72. Autocalculate(CalculateNode);
    73. break;
    74. case "Checkbox":
    75. Single TempSum = 0.0f;
    76. //找到该节点标识状态,如果是选择则去掉,没有选择则加上,同时计算和
    77. foreach (XmlNode CheckChildNode in ParentNode.ChildNodes)
    78. {
    79. if (CheckChildNode.Attributes["caption"].Value == StrCurrentNodeName)
    80. {
    81. TempNode = CheckChildNode;
    82. }
    83. }
    84. //添加select属性
    85. if (HasAttribute(TempNode, "select"))
    86. {
    87. if (TempNode.Attributes["select"].Value == "true")
    88. {
    89. //如果已经选择了,需要去掉选择
    90. TempNode.Attributes.Remove(TempNode.Attributes["select"]);
    91. }
    92. else
    93. {
    94. TempNode.Attributes["select"].Value = "true";
    95. }
    96. }
    97. else
    98. {
    99. XmlAttribute CheckSelectedAttr = XmlDoc.CreateAttribute("select");
    100. CheckSelectedAttr.Value = "true";
    101. TempNode.Attributes.Append(CheckSelectedAttr);
    102. }
    103. foreach (XmlNode CheckChildNode in ParentNode.ChildNodes)
    104. {
    105. if (HasAttribute(CheckChildNode, "select"))
    106. {
    107. TempSum += Convert.ToSingle(CheckChildNode.Attributes["value"].Value);
    108. }
    109. }
    110. //为父节点的value属性赋值
    111. ParentNode.Attributes["value"].Value = TempSum.ToString();
    112. //寻找父节点的父节点
    113. CalculateNode = ParentNode.ParentNode;
    114. //计算
    115. Autocalculate(CalculateNode);
    116. break;
    117. case "Textbox":
    118. //找到该节点修改Value
    119. BeSelectNode.Attributes["value"].Value = StrInputTextValue;
    120. //寻找本节点的父节点
    121. CalculateNode = BeSelectNode.ParentNode;
    122. //计算
    123. Autocalculate(CalculateNode);
    124. break;
    125. }
    126. }
    127. else
    128. {
    129. textBox1.Text += "提取属性值发生错误,没有找到对应节点或者属性值错误!" + Environment.NewLine;
    130. }
    131. }

      2、递归计算

    1. private void Autocalculate(XmlNode CalculateNode)
    2. {
    3. //在XML文档中,节点自动计算结果
    4. //CalculateResult MyCalcuteResult= new CalculateResult();
    5. float fSum = 0f;
    6. string StrID = "";
    7. string StrValue = "";
    8. string StrFormula = "";
    9. Boolean Continue = true;
    10. string StrFalse = "";
    11. //判断是否有子节点
    12. if (CalculateNode.HasChildNodes)
    13. {
    14. //有子节点需要看是否有计算公式,根据指定的节点进行自动计算
    15. if (HasAttribute(CalculateNode, "formula"))
    16. {
    17. //如果节点有formula属性,则提取出计算公式。
    18. StrFormula = GetAttrValue(CalculateNode, "formula");
    19. //将所有子节点的值进行替换完成后再进行计算。
    20. foreach (XmlNode MyNode in CalculateNode.ChildNodes)
    21. {
    22. if (HasAttribute(MyNode,"id"))
    23. {
    24. StrID = MyNode.Attributes["id"].Value;
    25. StrValue = MyNode.Attributes["value"].Value;
    26. if (StrValue.IsNullOrEmpty())
    27. {
    28. Continue = false;
    29. StrFalse = $"{StrID}为空";
    30. break;
    31. }
    32. else
    33. {
    34. //替换公式中的字符串,ID和值
    35. StrFormula = StrFormula.Replace(StrID, StrValue);
    36. }
    37. }
    38. else
    39. {
    40. Continue = false;
    41. }
    42. }
    43. if (Continue)
    44. {
    45. //进行计算获得结果
    46. fSum = GetFormulaResult(StrFormula);
    47. }
    48. }
    49. else
    50. {
    51. //没有formula属性,计算结果等于所有子节点的和。
    52. foreach (XmlNode MyNode in CalculateNode.ChildNodes)
    53. {
    54. StrValue = MyNode.Attributes["value"].Value;
    55. if (StrValue.IsNullOrEmpty())
    56. {
    57. Continue = false;
    58. StrFalse = MyNode.Name +"的值为空";
    59. break;
    60. }
    61. else
    62. {
    63. fSum += Convert.ToSingle(StrValue);
    64. }
    65. }
    66. }
    67. if (Continue)
    68. {
    69. //修改本节点的Value属性
    70. CalculateNode.Attributes["value"].Value = fSum.ToString();
    71. }
    72. CalculateNode = CalculateNode.ParentNode;
    73. //if (CalculateNode.NodeType == XmlNodeType.Document)
    74. if(CalculateNode==null)
    75. {
    76. StrFalse = "没有了父节点";
    77. Continue = false;
    78. }
    79. //是否继续计算
    80. if (Continue)
    81. {
    82. Autocalculate(CalculateNode);
    83. }
    84. else
    85. {
    86. textBox1.Text += StrFalse+Environment.NewLine;
    87. }
    88. }
    89. }

      这个问题看似简单,实际上也的确不难,就是有一点麻烦,需要耐心去解决细节问题。

  • 相关阅读:
    colmap+openMVS稠密重建
    404 - File or directory not found.
    VS code常用插件
    java计算机毕业设计在线问答平台源码+系统+mysql数据库+lw文档+部署
    【TensorFlow2 之014】在 TF 2.0 中实现 LeNet-5
    第02章 前馈神经网络
    Python打包exe等高效工具Nuitka
    【代码随想录】【算法训练营】【第37天】 [56]合并区间 [738]单调递增的数字 [968]监控二叉树
    2022年新能源汽车行业分析
    前端研习录(10)——CSS相对定位、绝对定位、固定定位以及Z-index属性、opacity属性讲解及示例说明
  • 原文地址:https://blog.csdn.net/dawn0718/article/details/134210698