部分代码参考了CalcBinding
与之不同的是,我这个绑定有点儿花里胡哨,支持以下几种绑定
<!--可以写入简单的代码,但必须要完全限定名称--> <TextBlock Text="{e:Binding ElementName=window,Path='System.Convert.ToInt32((ActualWidth-2)*2.1)'}" /> <!--ViewModel中的计算--> <TextBlock Text="{e:Binding Path=A+B/2+Person.Age}" Background="Red"/> <!--ViewModel与xaml混合计算--> <Button Content="change" x:Name="btn" Command="{Binding ChangedCommand}" Width="{e:Binding ElementName=window,Path='Width/2+B'}" /> <!--单独绑定xaml--> <TextBlock Text="{e:Binding ElementName=window}" Width="{Binding ElementName=window,Path=Width}" /> <!--单独绑定ViewModel--> <TextBlock Text="{e:Binding Path=A}" />
但是有一点就是,不论是绑定viewmodel还是xaml,不支持设计时动态显示,但支持运行时热重载
同时支持例如三元表达式这样的,如果觉得分割符号不够的话,可以在属性SymbolParameters中进行添加
git 源码路径
现在说下思路
创建一个绑定标记扩展
首先和CalcBinding一样,将Binding的大部分属性公开出来
在ProvideValue方法内部,先将Path属性按运算符号进行分割出属性列表
如果属性列表小于2的话,则用使用单独的Binding去绑定
反之则使用MultiBinding去绑定
特别注意的是MultiBinding中的,要判断属性列表是否以字母开头,所以我这个绑定扩展绑定的属性必须是以字母开头的才可以绑定上
其次要注意在赋值ElementName的时候,要判断当前属性是否在当前Widnow的ElementName组件中是否存在,如果不存在,则不要赋值,它有可能是存在于ViewModel中
所以这里就是能将xaml属性与ViewModel属性进行混合绑定计算的关键了
然后就是在给MultiBinding赋值MultiValueConverter的时候,如果当前Window没有,就不要赋值了,因为在设计时是没有的,如果非要给,那么在设计时,会在Converter动态编译的时候提示已经引入了相同的dll,然后整个设计器都全是堆栈错误,但是运行时还是好的
在MultiValueConverter的Convert方法中,循环判断当前传递的值是否为错误值,是错误值的则不要,然后将每个属性的正确类型查找出来
最后进行动态编译内存程序集,同时将该方法和对象进行缓存,以便下次调用(不然每次都动态编译会性能会很拉胯)
最后.
刚开始时我是使用JS引擎去解析数学表达式,在原始代码中还有残留,但后面发现,在解析例如 Person.Age 这类属性时并不好处理,所以还是决定使用程序集动态编译