Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Value Converter to Evaluate User Equation Input

0.00/5 (No votes)
19 Jan 2012 1  
This value converter makes it very quick and easy to add the ability for the user to input equations in text boxes that are bound to numeric properties.

Introduction

I know there are many times that it would be nice to give the user the ability to enter an equation in a TextBox instead of letting the user calculate the value before entering it. I have worked on projects that have implemented this functionality.

History

I was working on a value converter that would allow the binding to a calculated value. This got me searching the internet to see what was available for evaluating a string. A number of ideas caught my eye, and I decided to use the JavaScript Eval() function. As I was working on this, I suddenly realized that I could actually do things in the opposite way, and get a value converter that would evaluate a string to return a calculated value to the binding element. It actually ended up working better than I thought.

Implementation

To use JavaScript, a JavaScript function must be created:

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

I have put this code into a file named JavascriptEvaluator.js.

Next, this must be complied into an assembly. This assembly will be created in the file JavascriptEvaluator.dll by executing the following command in the Visual Studio command prompt:

jsc /target:library JavascriptEvaluator.js

I have put this command in a batch file named JavascriptEvaluatorCompile.bat.

In the project using this function, references must be added to JavascriptEvaluator.js and Microsoft.Jscript. You will have to browse to the location of the JavascriptEvaluator.js file to add it, and the Microsoft.Jscript assembly is included in the Framework Assemblies.

The value converter is actually very simple since only the ConvertBack method has any significant code, and it has very little:

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;
        }
    }

Since there is no processing on the value from the ViewModel, the Convert method just returns the value argument. ConvertBack only has to pass the value argument converted to a string to the JavaScript Eval() method through the Evaluate method of an instance of the JavaScriptEvaluator class. If there is an exception because the string cannot be evaluated, an exception will be thrown, and then just pass null to the ViewModel. A nice thing is that the WPF TextBox automatically handles the problem with the null value being passed to the ViewModel; where the property is of type decimal or some other non-Nullable type, it changes the TextBox border to red. What is nice is that the equation in the TextBox can still be edited to correct errors.

Obviously this is not ideal because the value of the property in the ViewModel does not correspond to what is displayed, and the ViewModel does not know that there is an issue with the user entry.

Calculation Engine Options

The JavaScript solution, although fairly good for my original requirements, is not really that great for this. The most significant issue is support of transcendental functions: JavaScript supports the most important transcendental functions, and the Eval() function will process these, but they are case sensitive, and must be preceded by “Math.”. Another disadvantage is that it does not support the exponent operator (“^”)--instead the pow function must be used. Ideally, a better evaluator can be created, and there are a number of good ones that can be found on the internet, but I wanted to avoid tramping on toes. The Eval() function is very powerful, and can do a lot more than anybody will probably want. A little bit of work could fix the problems with the pow function and case sensitivity.

I found a number of other options on the internet:

All are good options, several would be better than mine since it would be more intuitive for the user.

The Example in Use

Here a user has entered an equation, but has not left the TextBox, therefore the value converter has not processed the equation to determine if it is invalid:

After leaving the TextBox, the value converter has determined that there is an error in the user input, and returned a null to the property, causing an error to be identified by the View:

Here the user is entering an equation with a transcendental function (this is before the focus leaves the TextBox):

After the focus leaves the TextBox, the following is displayed:

Fixing User Input Error Detection

The main problem with this approach is that there is no way for the ViewModel to know about equation errors if bound to a number property. There is also the problem that the View really does not know about the error either, for disabling continuation. This could be fixed by making the number Nullable, but then the value displayed to the user would be reset, so that the user would not have the ability to fix the equation, also, the red error border would not show. If knowing about input errors is important, then either the ViewModel will have to be enhanced, and a multi-value converter would need to be used, or move the evaluation into the property (in this case, would attempt to evaluate the user input in the Property Set using the JavaScript Eval() method), which will be the type of string.

The last solution is the one I think is best. If an indication is needed on the form for an error, then a value converter could be used for some attribute (border, background, etc.) that would set the property to the invalid value if the property is non-numeric. The ViewModel would know that the property is invalid because it is non-numeric. The only thing left would be to convert the property between a string value and the proper numeric value for the Model. This is a little more work, but not bad for the functionality added.

What I would say is that this is a case that Microsoft missed an obvious feature in WPF; there is obviously an error, but no way to pass this information to the ViewModel.

Warning

Right now the value converter is not very smart, and if this converter is bound to a whole number type  (e.g., Integer) format, and the Eval() method returns a floating or fixed point value, it will cause an error. This can easily be fixed by adding code that determines the type of the property being bound to, and does the conversion before returning the value. This will also be a problem for overflow.

Conclusion

This value converter makes it very quick and easy to add the ability for the user to input equations in text boxes that are bound to numeric properties. It has a problem in that if the user inputs an invalid equation, the ViewModel will not know that there is a problem, and so it is possible to continue with an invalid equation in the text box. It this is acceptable, then this is a great option.

Also, this project shows how to use the JavaScript Eval() method to evaluate a string, and this can be used in other ways that may be more appropriate for the application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here