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

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

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

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

    计算节点:汇总;

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

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

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

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

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

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

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

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

  1、点击节点

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

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

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

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

        private void treeView1_MouseDown(object sender, MouseEventArgs e)
        {
            //获取鼠标点击的位置
            TreeNode FocusNode = treeView1.GetNodeAt(e.Location);
            string StrCurrentFullPath = FocusNode.FullPath;
            string StrNodeType = "";
            if (FocusNode != null)
            {
                //获取鼠标点击的位置是否在节点的图标上
                //在Treeview中针对Radio、Checkbox、TextBook分别进行设置
                TreeViewHitTestInfo hitTestInfo = treeView1.HitTest(e.Location);
                if (hitTestInfo.Location == TreeViewHitTestLocations.Image)
                {
                    StrNodeType = FocusNode.Tag.ToString();
                    //鼠标点击了节点的图标
                    switch (StrNodeType)
                    {
                        case "Radio":
                            // 取消同级节点的选中状态
                            foreach (TreeNode node1 in FocusNode.Parent.Nodes)
                            {
                                if (node1 != FocusNode)
                                {
                                    node1.ImageKey = "Radio";
                                    node1.SelectedImageKey = "Radio";
                                }
                            }
                            // 设置当前节点为选中状态
                            FocusNode.ImageKey = "RadioChecked";
                            FocusNode.SelectedImageKey = "RadioChecked";
                            //在XML文档中处理
                            HandleNodeInfoAtXmlContent(StrCurrentFullPath, StrNodeType,"");//在文档中找到该节点并处理

                            //
                            break;
                        case "Checkbox":
                            if (FocusNode.ImageKey == "Checkbox")
                            {
                                FocusNode.ImageKey = "CheckboxChecked";
                                FocusNode.SelectedImageKey = "CheckboxChecked";
                            }
                            else
                            {
                                FocusNode.ImageKey = "Checkbox";
                                FocusNode.SelectedImageKey = "Checkbox";
                            }
                            //在XML文档中处理
                            HandleNodeInfoAtXmlContent(StrCurrentFullPath, StrNodeType,"");//在文档中找到该节点并处理

                            break;
                        case "Textbox":
                            string StrMin = "";
                            string StrMax = "";
                            string StrMemo = "";
                            float fTemp;
                            ToTextboxInputWinPara.fMax = 0;
                            ToTextboxInputWinPara.fMin = 0;
                            ToTextboxInputWinPara.StrMemo = "";
                            FrmTextBoxInput FTI= new FrmTextBoxInput();
                            DialogResult result= FTI.ShowDialog();
                            if(result == DialogResult.OK)
                            {
                                StrCurrentTextboxValue = FTI.StrReturn;
                            }
                            //在XML文档中处理
                            HandleNodeInfoAtXmlContent(StrCurrentFullPath, StrNodeType, StrCurrentTextboxValue);//在文档中找到该节点并处理
                            break;
                    }
                    treeView1.Invalidate();
                }

                if (hitTestInfo.Location == TreeViewHitTestLocations.Label)
                {
                    //点击标签
                    if (FocusNode.Tag != null)
                    {
                        switch (FocusNode.Tag.ToString())
                        {
                            case "Radio":
                                if (FocusNode.ImageKey == "RadioChecked")
                                {
                                    FocusNode.SelectedImageKey = "RadioChecked";
                                }
                                if (FocusNode.ImageKey == "Radio")
                                {
                                    FocusNode.SelectedImageKey = "Radio";
                                }
                                break;
                            case "Checkbox":
                                if (FocusNode.ImageKey == "Checkbox")
                                {
                                    FocusNode.SelectedImageKey = "Checkbox";
                                }
                                if (FocusNode.ImageKey == "CheckboxChecked")
                                {
                                    FocusNode.SelectedImageKey = "CheckboxChecked";
                                }
                                break;
                            default: break;
                        }
                        treeView1.Invalidate();
                    }
                }
            }
        }

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

        private void HandleNodeInfoAtXmlContent(string StrCurrentFullPath,string StrNodeType,string StrInputTextValue)
        {
            //在XML文档内容中处理节点信息,传入参数:StrCurrentFullPath是当前点击选择的节点全路径名称
            int FirstIndex = StrCurrentFullPath.IndexOf("\\");
            int LastIndex = StrCurrentFullPath.LastIndexOf("\\");
            string StrCurrentNodeName = StrCurrentFullPath.Substring(LastIndex + 1);
            //提取父节点的名称
            string[] SubStr= StrCurrentFullPath.Split("\\");
            string ParentStr = SubStr[SubStr.Length - 2];
            // 使用XPath表达式定位到具体的节点,点击的节点名称是caption值
            string XpathExpression="";
            XmlNode CalculateNode=null;//计算节点
            switch (StrNodeType)
            {
                case "Radio":
                    XpathExpression = "//" + ParentStr + "/option[@caption='" + StrCurrentNodeName + "']";
                    break;
                case "Checkbox":
                    XpathExpression = "//" + ParentStr + "/input[@caption='" + StrCurrentNodeName + "']";
                    break;
                case "Textbox":
                    XpathExpression = "//" + ParentStr + "/"+ StrCurrentNodeName;
                    break;
            }
            XmlNode BeSelectNode = XmlDoc.SelectSingleNode(XpathExpression);
            //得到父节点的全路径名
            string SParentPath = StrCurrentFullPath.Substring(0, LastIndex);
            //得到父节点
            XmlNode ParentNode = FindNodeAtXmlContentByFullPath(SParentPath);
            XmlNode TempNode = null;
            if (BeSelectNode != null && ParentNode!=null)
            {
                //根据节点类型处理本节点
                switch (StrNodeType)
                {
                    case "Radio":
                        string StrValue = "";
                        //找到该节点标识选中状态
                        foreach (XmlNode RadioChildNode in ParentNode.ChildNodes)
                        {
                            //单选,先将父节点下的子节点的select属性全部删除
                            if (RadioChildNode.Attributes["select"] != null)
                            {
                                RadioChildNode.Attributes.Remove(RadioChildNode.Attributes["select"]);
                            }
                            //找到子节点
                            if (RadioChildNode.Attributes["caption"].Value == StrCurrentNodeName)
                            {
                                TempNode = RadioChildNode;
                                StrValue = TempNode.Attributes["value"].Value;
                            }
                        }
                        //添加select属性
                        if (TempNode!=null)
                        {                                
                            if (HasAttribute(TempNode, "select"))
                            {
                                TempNode.Attributes["select"].Value = "true";
                            }
                            else
                            {
                                XmlAttribute RadioNodeAttr = XmlDoc.CreateAttribute("select");
                                RadioNodeAttr.Value = "true";
                                TempNode.Attributes.Append(RadioNodeAttr);
                            }
                        }
                        //为父节点的value属性赋值
                        ParentNode.Attributes["value"].Value = StrValue;
                        //寻找父节点的父节点
                        CalculateNode = ParentNode.ParentNode;
                        //计算
                        Autocalculate(CalculateNode);
                        break;
                    case "Checkbox":
                        Single TempSum = 0.0f;
                        //找到该节点标识状态,如果是选择则去掉,没有选择则加上,同时计算和
                        foreach (XmlNode CheckChildNode in ParentNode.ChildNodes)
                        {
                            if (CheckChildNode.Attributes["caption"].Value == StrCurrentNodeName)
                            {
                                TempNode = CheckChildNode;
                            }
                        }
                        //添加select属性
                        if (HasAttribute(TempNode, "select"))
                        {
                            if (TempNode.Attributes["select"].Value == "true")
                            {
                                //如果已经选择了,需要去掉选择
                                TempNode.Attributes.Remove(TempNode.Attributes["select"]);
                            }
                            else
                            {
                                TempNode.Attributes["select"].Value = "true";
                            }
                        }
                        else
                        {
                            XmlAttribute CheckSelectedAttr = XmlDoc.CreateAttribute("select");
                            CheckSelectedAttr.Value = "true";
                            TempNode.Attributes.Append(CheckSelectedAttr);
                        }

                        foreach (XmlNode CheckChildNode in ParentNode.ChildNodes)
                        {
                            if (HasAttribute(CheckChildNode, "select"))
                            {
                                TempSum += Convert.ToSingle(CheckChildNode.Attributes["value"].Value);
                            }
                        }
                        //为父节点的value属性赋值
                        ParentNode.Attributes["value"].Value = TempSum.ToString();
                        //寻找父节点的父节点
                        CalculateNode = ParentNode.ParentNode;
                        //计算
                        Autocalculate(CalculateNode);
                        break;
                    case "Textbox":
                        //找到该节点修改Value值
                        BeSelectNode.Attributes["value"].Value = StrInputTextValue;
                        //寻找本节点的父节点
                        CalculateNode = BeSelectNode.ParentNode;
                        //计算
                        Autocalculate(CalculateNode);
                        break;
                }
            }
            else
            {
                textBox1.Text += "提取属性值发生错误,没有找到对应节点或者属性值错误!" + Environment.NewLine;
            }
            
        }

  2、递归计算

        private void Autocalculate(XmlNode CalculateNode)
        {
            //在XML文档中,节点自动计算结果
            //CalculateResult MyCalcuteResult= new CalculateResult();
            float fSum = 0f;
            string StrID = "";
            string StrValue = "";
            string StrFormula = "";
            Boolean Continue = true;
            string StrFalse = "";
            //判断是否有子节点
            if (CalculateNode.HasChildNodes)
            {
                //有子节点需要看是否有计算公式,根据指定的节点进行自动计算
                if (HasAttribute(CalculateNode, "formula"))
                {
                    //如果节点有formula属性,则提取出计算公式。
                    StrFormula = GetAttrValue(CalculateNode, "formula");
                    //将所有子节点的值进行替换完成后再进行计算。
                    foreach (XmlNode MyNode in CalculateNode.ChildNodes)
                    {
                        if (HasAttribute(MyNode,"id"))
                        {
                            StrID = MyNode.Attributes["id"].Value;
                            StrValue = MyNode.Attributes["value"].Value;
                            if (StrValue.IsNullOrEmpty())
                            {
                                Continue = false;
                                StrFalse = $"{StrID}为空";
                                break;
                            }
                            else
                            {
                                //替换公式中的字符串,ID和值
                                StrFormula = StrFormula.Replace(StrID, StrValue);
                            }
                        }
                        else
                        {
                            Continue = false;
                        }
                    }
                    if (Continue)
                    {
                        //进行计算获得结果
                        fSum = GetFormulaResult(StrFormula);
                    }
                }
                else
                {
                    //没有formula属性,计算结果等于所有子节点的和。
                    foreach (XmlNode MyNode in CalculateNode.ChildNodes)
                    {
                        StrValue = MyNode.Attributes["value"].Value;
                        if (StrValue.IsNullOrEmpty())
                        {
                            Continue = false;
                            StrFalse = MyNode.Name +"的值为空";
                            break;
                        }
                        else
                        {
                            fSum += Convert.ToSingle(StrValue);
                        }
                    }
                }
                if (Continue)
                {
                    //修改本节点的Value属性
                    CalculateNode.Attributes["value"].Value = fSum.ToString();
                }
                CalculateNode = CalculateNode.ParentNode;
                //if (CalculateNode.NodeType == XmlNodeType.Document)
                if(CalculateNode==null)
                {
                    StrFalse = "没有了父节点";
                    Continue = false;
                }
                //是否继续计算
                if (Continue)
                {
                    Autocalculate(CalculateNode);
                }
                else
                {
                    textBox1.Text += StrFalse+Environment.NewLine;
                }
            }
        }

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

11-04 09:53