WPF实现强大的动态公式计算


数据库可以定义表不同列之间的计算公式,进行自动公式计算,但如何实现行上的动态公式计算呢?行由于可以动态扩展,在某些应用场景下将能很好的解决实际问题。

1、VS2012新建一个WPF应用程序WpfApp_DynCalc,并添加一个类DynCalc.cs,如下图:

 

2、编辑MainWindow.xaml,代码如下:

 1 <Window x:Class="WpfApp_DynCalc.MainWindow"
 2  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4  Title="WPF动态计算示例" Height="350" Width="525">
 5     <Grid>
 6         <Grid.Resources>
 7         <Style TargetType="DataGrid">
 8             <!--网格线颜色-->
 9             <Setter Property="CanUserResizeColumns" Value="false"/>
 10             <Setter Property="Background" Value="#E6DBBB" />
 11             <Setter Property="BorderBrush" Value="#d6c79b" />
 12             <Setter Property="HorizontalGridLinesBrush">
 13                 <Setter.Value>
 14                     <SolidColorBrush Color="#d6c79b"/>
 15                 </Setter.Value>
 16             </Setter>
 17             <Setter Property="VerticalGridLinesBrush">
 18                 <Setter.Value>
 19                     <SolidColorBrush Color="#d6c79b"/>
 20                 </Setter.Value>
 21             </Setter>
 22         </Style>
 23 
 24         <!--标题栏样式-->
 25         <!--<Style TargetType="DataGridColumnHeader" >  26  <Setter Property="Width" Value="50"/>  27  <Setter Property="Height" Value="30"/>  28  <Setter Property="FontSize" Value="14" />  29  <Setter Property="Background" Value="White" />  30  <Setter Property="FontWeight" Value="Bold"/>  31  </Style>-->
 32 
 33         <Style TargetType="DataGridColumnHeader">
 34             <Setter Property="SnapsToDevicePixels" Value="True" />
 35             <Setter Property="MinWidth" Value="0" />
 36             <Setter Property="MinHeight" Value="28" />
 37             <Setter Property="Foreground" Value="#323433" />
 38             <Setter Property="FontSize" Value="14" />
 39             <Setter Property="Cursor" Value="Hand" />
 40             <Setter Property="Template">
 41                 <Setter.Value>
 42                     <ControlTemplate TargetType="DataGridColumnHeader">
 43                         <Border x:Name="BackgroundBorder" BorderThickness="0,1,0,1" 
 44  BorderBrush="#e6dbba" 
 45  Width="Auto">
 46                             <Grid >
 47                                 <Grid.ColumnDefinitions>
 48                                     <ColumnDefinition Width="*" />
 49                                 </Grid.ColumnDefinitions>
 50                                 <ContentPresenter Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
 51                                 <Path x:Name="SortArrow" Visibility="Collapsed" Data="M0,0 L1,0 0.5,1 z" Stretch="Fill" Grid.Column="2" Width="8" Height="6" Fill="White" Margin="0,0,50,0" 
 52  VerticalAlignment="Center" RenderTransformOrigin="1,1" />
 53                                 <Rectangle Width="1" Fill="#d6c79b" HorizontalAlignment="Right" Grid.ColumnSpan="1" />
 54                                 <!--<TextBlock Background="Red">  55  <ContentPresenter></ContentPresenter></TextBlock>-->
 56                             </Grid>
 57                         </Border>
 58                     </ControlTemplate>
 59                 </Setter.Value>
 60             </Setter>
 61             <Setter Property="Height" Value="25"/>
 62         </Style>
 63         <!--行样式触发-->
 64         <!--背景色改变必须先设置cellStyle 因为cellStyle会覆盖rowStyle样式-->
 65         <Style TargetType="DataGridRow">
 66             <Setter Property="Background" Value="#F2F2F2" />
 67             <Setter Property="Height" Value="25"/>
 68             <Setter Property="Foreground" Value="Black" />
 69             <Style.Triggers>
 70                 <!--隔行换色-->
 71                 <Trigger Property="AlternationIndex" Value="0" >
 72                     <Setter Property="Background" Value="#e7e7e7" />
 73                 </Trigger>
 74                 <Trigger Property="AlternationIndex" Value="1" >
 75                     <Setter Property="Background" Value="#f2f2f2" />
 76                 </Trigger>
 77 
 78                 <Trigger Property="IsMouseOver" Value="True">
 79                     <Setter Property="Background" Value="LightGray"/>
 80                     <!--<Setter Property="Foreground" Value="White"/>-->
 81                 </Trigger>
 82 
 83                 <Trigger Property="IsSelected" Value="True">
 84                     <Setter Property="Foreground" Value="Black"/>
 85                 </Trigger>
 86             </Style.Triggers>
 87         </Style>
 88 
 89         <!--单元格样式触发-->
 90         <Style TargetType="DataGridCell">
 91             <Setter Property="Template">
 92                 <Setter.Value>
 93                     <ControlTemplate TargetType="DataGridCell">
 94                         <TextBlock TextAlignment="Center" VerticalAlignment="Center"  >
 95                            <ContentPresenter />
 96                         </TextBlock>
 97                     </ControlTemplate>
 98                 </Setter.Value>
 99             </Setter>
100             <Style.Triggers>
101                 <Trigger Property="IsSelected" Value="True">
102                     <!--<Setter Property="Background" Value="White"/> 103  <Setter Property="BorderThickness" Value="0"/>-->
104                     <Setter Property="Foreground" Value="Black"/>
105                 </Trigger>
106             </Style.Triggers>
107         </Style>
108         </Grid.Resources>
109 
110 
111         <DataGrid Name="dgrid" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="256" Width="498" AutoGenerateColumns="False" >
112             <DataGrid.Columns>
113                 <DataGridTextColumn Header="指标" Binding="{Binding Zb}" Width="118"/>
114                 <DataGridTextColumn Header="值" Binding="{Binding Value}" Width="100"/>
115                 <DataGridTextColumn Header="公式" Binding="{Binding Formula}" Width="188"/>
116             </DataGrid.Columns>
117         </DataGrid>
118         <Button Content="计 算" HorizontalAlignment="Left" Margin="419,281,0,0" VerticalAlignment="Top" Width="85" Click="Button_Click_1" Height="28" />
119         <TextBlock Name="lblResult" HorizontalAlignment="Left" Margin="28,281,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top"/>
120 
121     </Grid>
122 </Window>

3、编辑后台文件MainWindow.xaml.cs,代码如下:

 1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Text;  5 using System.Threading.Tasks;  6 using System.Windows;  7 using System.Windows.Controls;  8 using System.Windows.Data;  9 using System.Windows.Documents; 10 using System.Windows.Input; 11 using System.Windows.Media; 12 using System.Windows.Media.Imaging; 13 using System.Windows.Navigation; 14 using System.Windows.Shapes; 15 
16 using System.Collections.ObjectModel; 17 namespace WpfApp_DynCalc 18 { 19     /// <summary>
20     /// MainWindow.xaml 的交互逻辑 21     /// </summary>
22     public partial class MainWindow : Window 23  { 24         public MainWindow() 25  { 26  InitializeComponent(); 27 
28           
29             ObservableCollection<Dynformula> ofs = new ObservableCollection<Dynformula>(); 30             ofs.Add(new Dynformula { Zb = "A", Value = "1", Formula = "" }); 31             ofs.Add(new Dynformula { Zb = "B", Value = "2", Formula = "2*A+1" }); 32             ofs.Add(new Dynformula { Zb = "C", Value = "3", Formula = "B*B" }); 33             ofs.Add(new Dynformula { Zb = "D", Value = "4", Formula = "C-2" }); 34             ofs.Add(new Dynformula { Zb = "Z", Value = "5", Formula = "D+C" }); 35             this.dgrid.ItemsSource = ofs; 36          
37          
38  } 39 
40         private void Button_Click_1(object sender, RoutedEventArgs e) 41  { 42             this.lblResult.Text = "计算..."; 43              this.dgrid.ItemsSource=   DynCalc.CalcScript(ref this.dgrid); 44              this.lblResult.Text = "计算完成!"; 45           
46  } 47  } 48 
49     public class Dynformula 50  { 51         private string zb; 52 
53         public string Zb 54  { 55             get { return zb; } 56             set { zb = value; } 57  } 58         private string value; 59 
60         public string Value 61  { 62             get { return this.value; } 63             set { this.value = value; } 64  } 65         private string formula; 66 
67         public string Formula 68  { 69             get { return formula; } 70             set { formula = value; } 71  } 72 
73  } 74 }

3、编辑类DynCalc.cs,代码如下:

 1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Text;  5 using System.Threading.Tasks;  6 
 7 namespace WpfApp_DynCalc  8 {  9 using System.Collections.ObjectModel;  10 using System.Windows;  11 using System.Windows.Controls;  12 using System.Reflection;  13 using System.Globalization;  14 using Microsoft.CSharp;  15 using System.CodeDom;  16 using System.CodeDom.Compiler;  17    public static class DynCalc  18  {  19 
 20        public static ObservableCollection<Dynformula> CalcScript(ref DataGrid dgrid)  21  {  22          
 23            CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();  24            ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler();  25            CompilerParameters objCompilerParameters = new CompilerParameters();  26            objCompilerParameters.ReferencedAssemblies.Add("System.dll");  27            objCompilerParameters.GenerateExecutable = false;  28            objCompilerParameters.GenerateInMemory = true;  29            CompilerResults cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, GenerateCode(ref dgrid));  30            if (cr.Errors.HasErrors)  31  {  32                Console.WriteLine("编译错误:");  33                foreach (CompilerError err in cr.Errors)  34  {  35  Console.WriteLine(err.ErrorText);  36  }  37                return null;  38  }  39            else
 40  {  41                // 通过反射,调用实例
 42                Assembly objAssembly = cr.CompiledAssembly;  43                object objDynCalc = objAssembly.CreateInstance("DynamicCodeGenerate.RunScript");  44                //MethodInfo objMI = objHelloWorld.GetType().GetMethod("OutPut");  45                //Console.WriteLine(objMI.Invoke(objHelloWorld, null));
 46 
 47                ObservableCollection<Dynformula> ofsnew = new ObservableCollection<Dynformula>();  48                //循环datagrid进行公式计算并赋值
 49                for (int i = 0; i < dgrid.Items.Count; i++)  50  {  51                    Dynformula item = dgrid.Items[i] as Dynformula;  52                    if (item == null)  53  {  54                        break;  55  }  56                    string zb = item.Zb;  57                    PropertyInfo pinfo = objDynCalc.GetType().GetProperty(zb);  58                    if (pinfo != null && pinfo.CanRead) {  59                        //获取属性get值
 60                        object obj_Name = pinfo.GetValue(objDynCalc, null);  61                      // item.Value = obj_Name.ToString();
 62                        ofsnew.Add(new Dynformula { Zb = item.Zb, Value = obj_Name.ToString(), Formula = item.Formula});  63  }  64  }  65                return ofsnew;  66  }  67  }  68        /// <summary>
 69        /// 计算逻辑C#脚本动态构建  70        /// </summary>
 71        /// <param name="dgrid">存有指标以及指标计算公式的datagrid</param>
 72        /// <returns>C#脚本</returns>
 73         static string GenerateCode(ref DataGrid dgrid)  74  {  75             StringBuilder sb = new StringBuilder();  76             StringBuilder sb构建函数内容 = new StringBuilder();  77             sb.Append("using System;");  78  sb.Append(Environment.NewLine);  79             sb.Append("namespace DynamicCodeGenerate");  80  sb.Append(Environment.NewLine);  81             sb.Append("{");  82  sb.Append(Environment.NewLine);  83             sb.Append(" public class RunScript");  84  sb.Append(Environment.NewLine);  85             sb.Append(" {");  86             //------------------------------------------------------------
 87             for(int i=0;i<dgrid.Items.Count;i++)  88  {  89                 Dynformula item = dgrid.Items[i] as Dynformula;  90                 if (item == null)  91  {  92                     break;  93  }  94                 string zb = item.Zb;  95  sb.Append(Environment.NewLine);  96                 sb.AppendFormat(" public double _{0};", item.Zb);  97  sb.Append(Environment.NewLine);  98                 sb.AppendFormat(" public double {0}",item.Zb);  99  sb.Append(Environment.NewLine); 100                 sb.Append(" {"); 101  sb.Append(Environment.NewLine); 102               
103                 if (item.Formula.Trim() != "") 104  { 105                     sb.Append(" set{ "+item.Zb+"=value;}" ); 106  sb.Append(Environment.NewLine); 107                     sb.Append(" get{return "+ item.Formula + ";}"); 108  } 109                 else
110  { 111                     sb.Append(" set{ _" + item.Zb + "=value;}"); 112  sb.Append(Environment.NewLine); 113                     sb.Append(" get{return _"+item.Zb+";} "); 114  sb.Append(Environment.NewLine); 115                     sb构建函数内容.Append(" _" + item.Zb + "=" + item.Value); 116  } 117  sb.Append(Environment.NewLine); 118                 sb.Append(" }"); 119  sb.Append(Environment.NewLine); 120  } 121             //-------------------------------------------- 122             //构造函数进行赋值
123  sb.Append(Environment.NewLine); 124             sb.Append(" public RunScript()"); 125  sb.Append(Environment.NewLine); 126             sb.Append(" {"); 127  sb.Append(Environment.NewLine); 128             sb.AppendFormat(" {0};",sb构建函数内容.ToString()); 129  sb.Append(Environment.NewLine); 130             sb.Append(" }"); 131  sb.Append(Environment.NewLine); 132 
133             //----------------------------------------------
134  sb.Append(Environment.NewLine); 135             sb.Append(" public string OutPut()"); 136  sb.Append(Environment.NewLine); 137             sb.Append(" {"); 138  sb.Append(Environment.NewLine); 139             sb.Append(" return \"Hello world!\";"); 140  sb.Append(Environment.NewLine); 141             sb.Append(" }"); 142             //-----------------------------------------
143  sb.Append(Environment.NewLine); 144             sb.Append(" }"); 145  sb.Append(Environment.NewLine); 146             sb.Append("}"); 147 
148             string code = sb.ToString(); 149  Console.WriteLine(code); 150             return code; 151  } 152 
153  } 154 }


4、运行程序,其中值列的值为初始值,点击计算后会根据公式列配置进行动态计算,初始化界面如下:

 5、点击计算后界面如下:

可见现在的值是根据公式配置进行动态计算的。当然代码经过扩展还可以支持函数和简单的逻辑判断,如if ...else等。实现更强大的逻辑处理。

 

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM