Impossible WPF Part 2: Binding Expressions

简介: 原文 http://www.11011.net/wpf-binding-expressions Back in April I posted an idea for building an expression converter for use with MultiBindings.

原文 http://www.11011.net/wpf-binding-expressions

Back in April I posted an idea for building an expression converter for use with MultiBindings. This is a question I often see asked, but the answer is usually "go and write a once off converter" (for an example see the Negate converter in my Transition sample - how useful is that really?). More recently I noticed this sample on LearnWPF which provides single value simple arithmetic conversions. Unfortunately no one ever took the bait and implemented the multi-value converter, so I had to go and do it myself.

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:JScriptConverterx:Key="JScript"TrapExceptions="False" />

And wire it up as a single Binding:

<TextBlockText="{Binding ElementName=tb1, Path=Text,
    Converter={StaticResource JScript}, 
    ConverterParameter=Int32.Parse(values[0])/100.0"/>

Or as a MultiBinding:

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

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))
                    cp.ReferencedAssemblies.Add(assembly.Location);

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

            Assembly result = results.CompiledAssembly;

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

            evaluator = (Delegate.CreateDelegate(
                typeof(Evaluator),
                Activator.CreateInstance(eval),
                "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)
        {
            try
            {
                return evaluator(parameter.ToString(), values);
            }
            catch
            {
                if (trap)
                    return null;
                else
                    throw;
            }
        }

        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();
        }
    }
}
目录
相关文章
|
C#
WPF中Binding使用StringFormat格式化字符串方法
原文:WPF中Binding使用StringFormat格式化字符串方法 货币格式 // $123.46 货币格式,一位小数 // $123.5 前文字 //单价:$123.
2218 0
|
9月前
|
存储 自然语言处理 C#
WPF技术之Binding
WPF(Windows Presentation Foundation)是微软推出的一种用于创建应用程序用户界面的框架。Binding(绑定)是WPF中的一个重要概念,它用于在界面元素和数据源之间建立关联。通过Binding,可以将界面元素(如文本框、标签、列表等)与数据源(如对象、集合、属性等)进行绑定,从而实现数据的双向传递和同步更新。
150 2
WPF技术之Binding
|
9月前
|
C#
WPF-Binding问题-模板样式使用Binding TemplatedParent与TemplateBinding区别
WPF-Binding问题-模板样式使用Binding TemplatedParent与TemplateBinding区别
104 0
|
C#
WPF QuickStart系列之数据绑定(Data Binding)
原文:WPF QuickStart系列之数据绑定(Data Binding) 这篇博客将展示WPF DataBinding的内容。 首先看一下WPF Data Binding的概览, Binding Source可以是任意的CLR对象,或者XML文件等,Binding Target需要有依赖属性。
1182 0
|
C#
解答WPF中ComboBox SelectedItem Binding不上的Bug
原文:解答WPF中ComboBox SelectedItem Binding不上的Bug 正在做一个打印机列表,从中选择一个打印机(System.Printing) var printServer = new LocalPrintServer(); PrintQueues = printServer.
973 0
|
C#
WPF Binding Mode,UpdateSourceTrigger
原文:WPF Binding Mode,UpdateSourceTrigger WPF 绑定模式(mode) 枚举值有5个1:OneWay(源变就更新目标属性)2:TwoWay(源变就更新目标并且目标变就更新源)3:OneTime(只根据源来设置目标,以后都不会变)4:OneWayToSource...
1530 0
|
C# .NET 开发框架
WPF笔记 ( xmlns引用,Resource、Binding 前/后台加载,重新绑定) 2013.6.7更新
原文:WPF笔记 ( xmlns引用,Resource、Binding 前/后台加载,重新绑定) 2013.6.7更新 1、xmlns Mapping URI的格式是 clr-namespace:[;assembly=] (1)如果自定义类和XAML处在同一个Assembly之中,只还需要提供clr-namespace值。
1409 0
|
C# 数据格式 XML
【WPF】动态设置Binding的ConverterParameter转换器参数
原文:【WPF】动态设置Binding的ConverterParameter转换器参数 问题:XAML中,想要在一个Bingding语句中再次Bingding。
4470 0
|
XML .NET 程序员
WPF Binding学习(四) 绑定各种数据源
转自:http://blog.csdn.net/lisenyang/article/details/18312199 1.集合作为数据源    首先我们先创建一个模型类 public class Student { public int ID { get; set; } ...
1392 0