Impossible WPF Part 2: Binding Expressions

The result is the JScriptConverter, which for simplicity of use is both an IMultiValueConverter and an IValueConverter. There are numerous benefits to using JScript here:

  1. It’s part of the framework, so we don’t have to go and write our own scanner/parser/interpreter for a new mini language.
  2. It’s a dynamic language, so it nicely complements the dynamically typed binding system of WPF.
  3. Evaluations in JScript are interpreted not compiled, so we don’t leak memory on every evaluation.

The code for the conveter is once again quite trivial. Because it’s quite difficult to set up a JScript environment for evaluation I instead compile a tiny JScript assembly to wrap the evaluations. This is done once statically and then reused by all instances of the class. Note the TrapExceptions property, if this is set to true any exceptions raised during the conversion will result in null.

Using it is easy. Just instantitate the converter as a resource:

<utils:JScriptConverter x:Key="JScript" TrapExceptions="False" />

And wire it up as a single Binding:

<TextBlock Text="{Binding ElementName=tb1, Path=Text,
    Converter={StaticResource JScript}, 

Or as a MultiBinding:

<MultiBinding Converter="{StaticResource JScript}" 
            "new System.Windows.Thickness(values[0]*0.1/2,values[1]*0.1/2,0,0)">
    <Binding RelativeSource="{RelativeSource TemplatedParent}" 
        Path="ActualWidth" />
    <Binding RelativeSource="{RelativeSource TemplatedParent}" 
        Path="ActualHeight" />

I’m not so happy with the code that enumerates and adds references to all the Assemblies in the current AppDomain, but for the time being it serves its purpose.

Anyway, without further ado (of course this requires a reference to Microsoft.JScript). Cut and paste as you please:

using System;
using System.Windows.Data;
using System.CodeDom.Compiler;
using System.Reflection;

namespace Utils.Avalon
    public sealed class JScriptConverter : IMultiValueConverter, IValueConverter
        private delegate object Evaluator(string code, object[] values);
        private static Evaluator evaluator;

        static JScriptConverter()
            string source = 
                @"import System; 

                class Eval
                    public function Evaluate(code : String, values : Object[]) : Object
                        return eval(code);

            CompilerParameters cp = new CompilerParameters();
            cp.GenerateInMemory = true;
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                if (System.IO.File.Exists(assembly.Location))

            CompilerResults results = (new Microsoft.JScript.JScriptCodeProvider())
                .CompileAssemblyFromSource(cp, source);

            Assembly result = results.CompiledAssembly;

            Type eval = result.GetType("Eval");

            evaluator = (Delegate.CreateDelegate(
                "Evaluate") as Evaluator);

        private bool trap = false;
        public bool TrapExceptions
            get { return this.trap; }
            set { this.trap = true; }

        public object Convert(object[] values, System.Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
                return evaluator(parameter.ToString(), values);
                if (trap)
                    return null;

        public object Convert(object value, Type targetType, 
            object parameter, System.Globalization.CultureInfo culture)
            return Convert(new object[] { value }, targetType, parameter, culture);

        public object[] ConvertBack(object value, System.Type[] targetTypes,
            object parameter, System.Globalization.CultureInfo culture)
            throw new System.NotSupportedException();

        public object ConvertBack(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
            throw new System.NotSupportedException();

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.