动态绑定方程式到文本框

在开发应用程序时,经常需要处理用户输入的数值。通常情况下,用户需要先计算出结果,然后再输入。但是,如果能够允许用户直接在文本框中输入方程式,然后自动计算结果并绑定到数值属性,那将大大提高用户体验。本文将介绍如何实现这一功能。

在之前的项目中,实现了一个值转换器,它允许绑定到一个计算后的值。这让开始在互联网上寻找能够评估字符串的方法。注意到了一些有趣的想法,并决定使用JavaScript的Eval()函数。在实现过程中,突然意识到,可以反过来做,得到一个值转换器,它可以评估一个字符串并返回计算后的值给绑定元素。结果比预想的要好。

实现方法

要使用JavaScript,首先需要创建一个JavaScript函数:

package JavascriptEvaluator { class JavascriptEvaluator { public function Evaluate(expr: String): String { return eval(expr, "unsafe"); } } }

将这段代码放入了一个名为JavascriptEvaluator.js的文件中。接下来,需要将这个文件编译成一个程序集。这个程序集将在执行以下命令后创建在JavascriptEvaluator.dll文件中:

jsc /target:library JavascriptEvaluator.js

将这个命令放入了一个名为JavascriptEvaluatorCompile.bat的批处理文件中。

在使用这个函数的项目中,需要添加对JavascriptEvaluator.js和Microsoft.Jscript的引用。需要浏览到JavascriptEvaluator.js文件的位置来添加它,而Microsoft.Jscript程序集包含在框架程序集中。

值转换器

值转换器非常简单,因为只有ConvertBack方法包含重要的代码,而且代码量很少:

class EvaluationValueConverter : IValueConverter { private readonly JavascriptEvaluator.JavascriptEvaluator evaluator = new JavascriptEvaluator.JavascriptEvaluator(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) return null; try { return evaluator.Evaluate(value.ToString()); } catch (Exception e) { return null; } } }

由于ViewModel中没有对值进行处理,Convert方法只是返回值参数。ConvertBack只需要将值参数转换为字符串,然后通过JavaScriptEvaluator类的Evaluate方法传递给JavaScript Eval()方法。如果因为字符串无法评估而抛出异常,那么就会抛出异常,然后向ViewModel传递null。一个很好的特点是WPF TextBox会自动处理传递给ViewModel的null值的问题;如果属性是decimal或其他非Nullable类型,它会将TextBox边框变为红色。这样,用户仍然可以编辑TextBox中的方程式以纠正错误。

计算引擎选项

虽然JavaScript解决方案对于最初的需求来说相当不错,但并不是很好。最显著的问题是不支持超越函数:JavaScript支持最重要的超越函数,Eval()函数也会处理这些函数,但它们是大小写敏感的,并且必须以“Math.”开头。另一个缺点是它不支持指数运算符(“^”)——相反,必须使用pow函数。理想情况下,可以创建一个更好的评估器,互联网上可以找到许多好的评估器,但不想冒犯别人。Eval()函数非常强大,可以做比任何人都想要的更多的事情。稍微修改一下就可以解决pow函数和大小写敏感的问题。

在互联网上找到了一些其他的选项:

  • Calculator.NET - 一个计算数学表达式的计算器()。喜欢这个,因为它也可以计算字符串,但这对本应用并不重要,它还可以进行转换。它还有一个前端,可以很好地替代标准计算器。
  • 在运行时编译C#代码以评估数学表达式()。这个选项将方程式编译成C#。可能不是最好的选择,因为它有很多JavaScript方法的问题,而且编译后的代码可能会有垃圾回收的问题。
  • 使用正则表达式的C#公式评估器()。真的很喜欢这个选项,特别是因为它很容易扩展。它将是所有选项中最慢的,因为需要评估所有的正则表达式,但对于用户输入来说,可能不是什么大问题。
  • “表达式评估器重访”()。另一个用C#编写的实现。
  • Flee(快速轻量级表达式评估器)()。

这些都是很好的选择,有几个会比更好,因为它们对用户来说更直观。

示例使用

这里用户输入了一个方程式,但还没有离开TextBox,所以值转换器还没有处理方程式以确定它是否无效:

在离开TextBox后,值转换器确定用户输入有错误,并返回null给属性,导致视图识别出错误:

这里用户正在输入一个带有超越函数的方程式(这是在焦点离开TextBox之前):

焦点离开TextBox后,显示如下:

修复用户输入错误检测

这种方法的主要问题是,如果绑定到数值属性,ViewModel无法知道方程式错误。还有一个问题是,视图也不知道错误,无法禁用继续。这可以通过使数字可空来修复,但然后显示给用户的价值将被重置,用户将无法修复方程式,而且红色错误边框也不会显示。如果知道输入错误很重要,那么要么ViewModel需要增强,需要使用多值转换器,要么将评估移到属性中(在这种情况下,将尝试在属性设置中使用JavaScriptEval()方法评估用户输入),这将是字符串类型。

认为最后一个解决方案是最好的。如果需要在表单上指示错误,那么可以使用值转换器设置一些属性(边框、背景等),如果属性是非数值的,就将属性设置为无效值。ViewModel会知道属性无效,因为它是非数值的。剩下的就是将属性在字符串值和适当的数值之间转换以供模型使用。这需要更多的工作,但对于添加的功能来说并不坏。

警告

目前,值转换器并不智能,如果这个转换器绑定到一个整数类型(例如,Integer)的格式,而Eval()方法返回一个浮点或定点值,它将导致错误。这可以通过添加代码来确定正在绑定的属性的类型,并在返回值之前进行转换来轻松修复。这也将是溢出的问题。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485