C#.NET 自定计算公式,生成字符串并解析计算

目的:

  可由用户自定计算公式,具体使用背景就不说了,看懂的话可以借鉴。

先看看效果吧。

 

不会描述,直接上代码吧

    /// <summary>
    /// 标记属性为可进行计算
    /// </summary>
    public class ComputerAttribute: Attribute
    {
    }

  

    public class Cale
    {
        [Description("主键")]
        public int Id { get; set; }
        [Description("姓名")]
        public string Name { get; set; }
        [Description("重量")]
        [Computer]
        public decimal Weight { get; set; }
        [Description("税率")]
        [Computer]
        public decimal Rage { get; set; }
        [Description("人工费")]
        [Computer]
        public decimal RenGong { get; set; }
        [Description("单价")]
        [Computer]
        public decimal Price { get; set; }
        [Description("数量")]
        [Computer]
        public int Qty { get; set; }
    }

  

    public class SomeHelper
    {
        /// <summary>
        /// 计算返回值
        /// </summary>
        /// <param name="expression">公式字符串</param>
        /// <returns></returns>
        public static float CalcByJs(string expression)
        {
            Microsoft.JScript.Vsa.VsaEngine ve = Microsoft.JScript.Vsa.VsaEngine.CreateEngine();
            object returnValue = Microsoft.JScript.Eval.JScriptEvaluate((object)expression, ve);
            return float.Parse(returnValue.ToString());
        }
        public static string CalcByTable(string exp)
        {
            DataTable dt = new DataTable();
            return  dt.Compute(exp, "false").ToString();
        }
    }

  

 

UI用了一些DevExpress控件

 

WebForm后台代码:

     /// <summary>
        /// 进行公式组装的对象
        /// </summary>
        public object CaleObject { get; set; }
        /// <summary>
        /// 获取传入对象的属性,有ComputerAttribute表示可以用于计算
        /// </summary>
        /// <returns></returns>
        public List<BindingKV> GetDataSrouce()
        {
            List<BindingKV> data = new List<BindingKV>();
            var properties=  CaleObject.GetType().GetProperties();
            foreach (PropertyInfo item in properties)
            {
                var cptAttr= item.GetCustomAttributes().FirstOrDefault(a=>a.GetType()==typeof(ComputerAttribute));
                if (cptAttr != null)
                {
                    object[] attr = item.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    //描述特性
                    string description = attr.Length == 0 ? item.Name : ((DescriptionAttribute)attr[0]).Description;
                    data.Add(new BindingKV { EditValue=item.Name,TextValue=description });
                }
            }
            return data;
        }

模拟数据用于测试:

        /// <summary>
        /// 构造方法
        /// </summary>
        public CalculateExpression()
        {
            InitializeComponent();
            CaleObject = new Cale {
                Id=4, Name="Jack",Price=5.6m, Qty=343, Rage=0.17m, RenGong=200, Weight=50
            };
        }

listbox绑定数据源

  private void CalculateExpression_Load(object sender, EventArgs e)
        {
            this.listBox1.ValueMember = "EditValue";
            this.listBox1.DisplayMember = "TextValue";
            this.listBox1.DataSource= GetDataSrouce();
        }

差点忘了这2个:

    public enum FiledType
    {
        /// <summary>
        /// 字段
        /// </summary>
        Filed,
        /// <summary>
        /// 运算符
        /// </summary>
        Flag,
        /// <summary>
        /// 常量
        /// </summary>
        Constant
    }
    /// <summary>
    /// ListBox Item绑定的类
    /// </summary>
    public class BindingKV
    {
        /// <summary>
        /// ValueMember绑定的属性
        /// </summary>
        public string EditValue { get; set; }
        /// <summary>
        /// DisplayMember绑定的属性
        /// </summary>
        public string TextValue { get; set; }
    }

添加公式按钮CheckButton到panel里,listbox是双击选中的添加,常量的话就是ButtonEdit的button ,Kind=Plus,其他运算符就是各个按钮啦;

        /// <summary>
        /// 加号,其他运算符一样的,改为相应的符号就行了
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            CommonAddCtl("+", "+", FiledType.Flag);
        }

     /// <summary>
        /// 添加常量,ButtonEdit Mask设置Numeric ,n2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textEdit1_ButtonClick(object sender, DevExpress.XtraEditors.Controls.ButtonPressedEventArgs e)
        {
            if (e.Button.Kind == DevExpress.XtraEditors.Controls.ButtonPredefines.Plus)
            {
                if (!string.IsNullOrWhiteSpace(textEdit1.Text))
                {
                    CommonAddCtl(textEdit1.Text, textEdit1.Text, FiledType.Constant);
                }
            }
        }
       /// <summary>
        /// 字段双击添加
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listBox1_DoubleClick(object sender, EventArgs e)
        {
            if (this.listBox1.SelectedIndex >= 0)
            {
                BindingKV selected = (BindingKV)this.listBox1.SelectedItem;
                CommonAddCtl(selected.TextValue, selected.EditValue, FiledType.Filed);
            }
        }

  

     /// <summary>
        /// 统一添加控件的方法
        /// </summary>
        /// <param name="text"> CheckButton控件显示文本</param>
        /// <param name="tag">CheckButton的Tag属性,存放属性名</param>
        /// <param name="type"></param>
        public void CommonAddCtl(string text, string tag, FiledType type)
        {
            Point p = GetPoint(this.CtlPanel);

            CheckButton btn = new CheckButton
            {
                Text = text,
                ButtonStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder,
                AutoSize = true,
  
            };
            if (type == FiledType.Filed)
            {
                btn.Font = new Font("", 11f, FontStyle.Underline);
                btn.Tag = "{" + tag +"}";
            }
            else
            {
                btn.Font = new Font("", 11f, FontStyle.Regular);
                btn.Tag = tag ;
            }
            btn.Location = p;
     
            btn.CheckedChanged += Btn_CheckedChanged;
            CtlPanel.Controls.Add(btn);
        }
        /// <summary>
        /// 只能选中一个
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Btn_CheckedChanged(object sender, EventArgs e)
        {
            CheckButton btn = ((CheckButton)sender);
            if (btn.Checked)
            {
                foreach (Control item in CtlPanel.Controls)
                {
                    if (item is CheckButton)
                    {
                        if (item.GetHashCode() != btn.GetHashCode())
                        {
                            ((CheckButton)item).Checked = false;
                        }
                    }
                }
            }
        }

  

删除、全删panel里的CheckButton,删除要选中上面要删除的CheckButton

         /// <summary>
        /// 删除
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button9_Click(object sender, EventArgs e)
        {
            foreach (Control item in CtlPanel.Controls)
            {
                if (item is CheckButton)
                {
                    if (((CheckButton)item).Checked)
                    {
                        CtlPanel.Controls.Remove(item);
                    }
                }
            }

        }
        /// <summary>
        /// 全删
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button10_Click(object sender, EventArgs e)
        {
            CtlPanel.Controls.Clear();
        }    

panel里添加、删除CheckButton,更新对于label的公式,这里的公式是我们需要的,上面那个公式给用户用(看)的,晕~~~

        /// <summary>
        /// panel里添加控件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CtlPanel_ControlAdded(object sender, ControlEventArgs e)
        {
            ChangeExpression();
        }
      

      /// <summary>
      /// 获取所有CheckButton,拼接Tag属性,更新到label中
      /// </summary>

        private void ChangeExpression()
        {
            string expression = string.Empty;
            if (this.CtlPanel.Controls.Count < 1)
            {
                this.label1.Text = string.Empty;
            }else
            {
                foreach (var item in this.CtlPanel.Controls)
                {
                    if(item is CheckButton)
                    {
                        expression+=((CheckButton)item).Tag.ToString();
                    }
                }
                this.label1.Text = expression;
            }
        }
        /// <summary>
        /// panel里删除控件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CtlPanel_ControlRemoved(object sender, ControlEventArgs e)
        {
            ChangeExpression();
        }

最后,拿到公式,上面我们把{属性名称}加到公式中了,现在就通过正则匹配,获取对于属性的值,把数值替换到公式里,然后调用上面的CalcByJs()方法进行计算; 

        /// <summary>
        /// 计算
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button11_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(this.label1.Text))
            {
                MessageBox.Show("没有添加公式哦");
                return;
            }
            //获取公式
            string gongShi = this.label1.Text;
            string pi = @"{[^+-/*%]+}";
            Regex reg = new Regex(pi);

            var get = reg.Matches(gongShi);
            //通过匹配公式,获取参与计算的字段
            List<string> fileds = new List<string>();
            foreach (Match item in get)
            {
                fileds.Add(item.Value);
            }
            #region 通过反射,获取对象属性值,替换成可进行计算的数值公式
            var properties = CaleObject.GetType().GetProperties();
            foreach (string filed in fileds)
            {
                var prop = properties.FirstOrDefault(a => "{" + a.Name + "}" == filed);
                string value = prop.GetValue(CaleObject).ToString();
                gongShi = gongShi.Replace(filed, value);
            }
            #endregion
            //执行计算
            try
            {
                this.label1.Text = gongShi;
                //float result= SomeHelper.CalcByJs(gongShi);
                //if (result.Equals(float.PositiveInfinity))
                //{
                //    MessageBox.Show("除数不能为零");
                //    return;
                //}

                //MessageBox.Show(result.ToString());
                MessageBox.Show(SomeHelper.CalcByTable(gongShi));
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

  

最后总结:

 

posted @ 2020-08-29 17:50  独立思考者  阅读(3454)  评论(1)    收藏  举报