wpf xaml 中的计算绑定不用convert
安装:
Install-Package CalcBinding
基本使用方法:
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
示例:
<TextBox xmlns:syswin="clr-namespace:System.Windows;assembly=PresentationFramework" Width="200" Height="30" Text="{c:Binding syswin:SystemParameters.CaptionHeight * 2, Mode=OneWay, StringFormat='{}{0:F0}'}" />
可以注册命名空间到全文,也可以如上临时引入。
以下为官方文档翻译而来
CalcBinding 是一个高级绑定标记扩展,它允许你在 xaml 中编写计算的绑定表达式,而无需自定义转换器。CalcBinding 可以自动执行 bool 到可见性转换、不同的代数运算、反转表达式等等。CalcBinding 使绑定表达式更短且更用户友好。发行说明
CalcBinding 在 NuGet 中提供。您可以使用以下方法安装包:
PM> Install-Package CalcBinding
以下示例显示了在非常简单的情况下具有 standart Binding 和 CalcBinding 的 xaml 代码片段:
<Label>
<Label.Content>
<MultiBinding Conveter={x:StaticResource MyCustomConverter}>
<Binding A/>
<Binding B/>
<Binding C/>
</MultiBinding>
</Label.Content>
</Label>
(没有 MyCustomConveter 声明并在 XAML 中引用它)
<Label Content="{c:Binding A+B+C }" />
- Path 中具有许多可用运算符的一个或多个源属性:description
<Label Content="{c:Binding A*0.5+(B.NestedProp1/C - B.NestedProp2 % C) }" />
<c:Binding 'A and B or C' />
- Path: description 中的一个或多个静态属性
<TextBox Text="{c:Binding 'local:StaticClass.Prop1 + local:OtherStaticClass.NestedProp.PropB + PropC'}"/>
<Button Background="{c:Binding '(A > B ? media:Brushes.LightBlue : media:Brushes.White)'}"/>
- Path: description 中类 System.Math 的属性和方法
<TextBox Text="{c:Binding 'Math.Sin(Math.Cos(A))'}"/>
- 枚举类型,如 Path: description 中的常量或 source 属性
<TextBox Text="{c:Binding '(EnumValue == local:CustomEnum.Value1 ? 10 : 20)'}"/>
- 如果可能,自动反转绑定表达式:description
<TextBox Text = "{c:Binding 'Math.Sin(A*2)-5'}"/> {two way binding will be created}
- 如果 target 属性具有这样的类型,则自动将 bool 表达式转换为 Visibility,并返回 target: description
<Button Visibility="{c:Binding !IsChecked}" />
<Button Visibility="{c:Binding IsChecked, FalseToVisibility=Hidden}" />
您可以编写任何代数、逻辑和字符串表达式,其中包含源属性路径、字符串、数字、类 Math 的所有成员和以下运算符:
"(", ")", "+", "-", "*", "/", "%", "^", "!", "&&","||",
"&", "|", "?", ":", "<", ">", "<=", ">=", "==", "!="};
和三元运算符,形式为 'bool_expression ? expression_1 : expression_2'
应该知道,xaml 通常是 xml 格式,并且 xml 在设置属性值时不支持使用以下符号:&、<。因此,CalcBinding 支持包含这些符号的运算符的以下别名:
| 算子 | 别名 | 评论 |
|---|---|---|
| && | 和 | |
| || | 或 | 不是 nessesary,只是为了对称 |
| < | 少 | |
| <= | 更少= |
<TextBox Text="{c:Binding A+B+C}"/>
<TextBox Text="{c:Binding A-B-C}"/>
<TextBox Text="{c:Binding A*(B+C)}"/>
<TextBox Text="{c:Binding 2*A-B*0.5}"/>
<TextBox Text="{c:Binding A/B, StringFormat={}{0:n2} --StringFormat is used}"/> {with string format}
<TextBox Text="{c:Binding A%B}"/>
<TextBox Text="{c:Binding '(A == 1) ? 10 : 20'}"/> {ternary operator}
<CheckBox Content="!IsChecked" IsChecked="{c:Binding !IsChecked}"/>
<TextBox Text="{c:Binding 'IsChecked and IsFull'}"/> {'and' is equvalent of '&&'}
<TextBox Text="{c:Binding '!IsChecked or (A > B)'}"/> {'or' is equvalent of '||', but you can leave '||'}
<TextBox Text="{c:Binding '(A == 1) and (B less= 5)'}"/> {'less=' is equvalent of '<='}
<TextBox Text="{c:Binding (IsChecked || !IsFull)}"/>
- 构成源属性路径的标识符应使用三元运算符中的任何运算符或分隔符(单引号、空格等)与运算符 ':“ 分隔:
<TextBox Text="{c:Binding '(A == 2)?IsChecked : IsFull}"/> <!-- right -->
<TextBox Text="{c:Binding '(A == 2)?IsChecked :!IsFull}"/> <!-- right -->
<TextBox Text="{c:Binding '(A == 2) ? IsChecked :4 + IsFull}"/> <!-- right -->
<TextBox Text="{c:Binding '(A == 2)?IsChecked:IsFull}"/> <!-- wrong -->
这种限制是由查找 static 属性的 path analyzer 工作引起的
从版本 2.3 开始,CalcBinding 支持绑定表达式中的静态属性。您可以编写以任何类的 static 属性开头的路径,并在 static 属性后面有任意数量的属性。CalcBinding 使用以下静态属性路径声明语法:
'xmlNamespace:Class.StaticProperty.NestedProperty' 等。
哪里:
-
xmlNamespace - 通常的 xml 命名空间,映射到具有其他命名空间定义的 xaml 文件标头中的普通命名空间。
-
Class - 存在于 xmlNamespace 映射的命名空间中的类的名称
-
StaticProperty - 类 Class 的静态属性
-
。NestedProperty 等 - StaticProperty 后面的属性链
<TextBox Text="{c:Binding 'local:Class.NestedProp.Prop1 + local:OtherStaticClass.PropB + PropC'}"/>
<Button Background="{c:Binding '(A > B ? media:Brushes.LightBlue : media:Brushes.White)'}"/>
其中 local 和 media 在 XAML 文件的标头中定义:
<<UserControl x:Class="WpfExample.FifthPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfExample"
xmlns:media ="clr-namespace:System.Windows.Media;assembly=PresentationCore">
...
</UserControl>
- 对于静态属性路径的非静态属性路径,应用以下规则:您应该在三元运算符的 ':' 运算符和构成静态属性路径的标识符(命名空间或属性)之间放置任何分隔符或运算符:
<TextBox Text="{c:Binding '(A == 2)?local:Class.Prop1 : local:Class.Prop2}"/> <!-- right -->
<TextBox Text="{c:Binding '(A == 2)?local:OtherClass.IsChecked :!local.OtherClass.IsFull}"/> <!-- right -->
<TextBox Text="{c:Binding '(A == 2) ? local:Class.A :4 + local:Class.B}"/> <!-- right -->
<TextBox Text="{c:Binding '(A == 2)?local:Class.Prop1: local:Class.Prop2}"/> <!-- wrong -->
<TextBox Text="{c:Binding '(A == 2)?local:OtherClass.IsChecked:local.OtherClass.IsFull}"/> <!-- wrong -->
<TextBox Text="{c:Binding '(A == 2) ? local:Class.A:4+local:Class.B}"/> <!-- wrong -->
您可以在 path 属性中以本机形式使用 System.Math 类的任何成员,就像您正在编写通常的 C# 代码一样:
<TextBox Text="{c:Binding Math.Sin(A*Math.PI/180), StringFormat={}{0:n5}}"/>
<TextBox Text="{c:Binding A*Math.PI}" />
- 尽管 CalcBinding 支持静态属性,但 Math 类是在支持静态属性之前创建和使用的独立功能。因此,不应将 static property 语法与 Math 类的成员一起使用。
<TextBox Text="{c:Binding A*Math.PI}" /> <!-- right -->
<TextBox Text="{c:Binding Math.Sin(10)+20}" /> <!-- right -->
<xmlns:sys="clr-namespace:System;assembly=mscorlib">
...
<TextBox Text="{c:Binding A*sys:Math.PI}" /> <!-- wrong -->
<TextBox Text="{c:Binding sys:Math.Sin(10)+20}" /> <!-- wrong -->
从版本 2.3 开始,CalcBinding 支持在绑定表达式中使用 Enums 表达式。您可以编写具有 Enum 类型的枚举值或属性(也可以是 static 属性)。CalcBinding 使用以下声明 enum value 的语法:
“xmlNamespace:EnumClass.Value”
哪里:
-
xmlNamespace - 通常的 xml 命名空间,映射到具有其他命名空间定义的 xaml 文件标头中的普通命名空间。
-
EnumClass - 存在于 xmlNamespace 映射的命名空间中的枚举类的名称
<CheckBox Content="Started" IsChecked="{c:Binding 'State==local:StateEnum.Start'}" />
<Button Background="{c:Binding 'EnumValue == local:MyEnum.Value1 ? media:Brushes.Green : media:Brushes.Red'}"/>
哪里
-
local 和 media 在 XAML 文件的标头中定义:
xml <<UserControl x:Class="WpfExample.FifthPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:local="clr-namespace:WpfExample" xmlns:media ="clr-namespace:System.Windows.Media;assembly=PresentationCore"> ... </UserControl> -
StateEnum, MyEnum - 自定义枚举
-
StateEnum.Start, MyEnum.Value1 - 自定义枚举的值
-
Brushes - 具有静态 Brush 属性的标准类
-
Brushes.Green, Brushes.Red - 类 Brush 的静态属性
- 对于 Enum 常量的静态属性路径,应用以下规则:您应该在三元运算符的 ':' 运算符和构成 Enum 路径的标识符(命名空间或属性)之间放置任何分隔符或运算符:
<TextBox Text="{c:Binding '(A == 2)?sys:Visibility.Visible : sys:Visibility.Hidden}"/> <!-- right -->
<TextBox Text="{c:Binding '(A == 2)?local:MyEnum.Value1 : local.MyEnum.Value2}"/> <!-- right -->
<TextBox Text="{c:Binding '(A == 2)?sys:Visibility.Visible:sys:Visibility.Hidden}"/> <!-- wrong -->
<TextBox Text="{c:Binding '(A == 2)?local:MyEnum.Value1: local.MyEnum.Value2}"/> <!-- wrong -->
<TextBox Text="{c:Binding '(A == 2)?local:MyEnum.Value1 :local.MyEnum.Value2}"/> <!-- wrong -->
例如,您必须使用 TextBox 的双属性 A 和 Content 属性从 viewModel 创建双向绑定。 TextBox.Content 通过以下公式依赖于属性 'A': '数学.Sin(A*2)-5'
您所要做的就是写下:
<TextBox Text = "{c:Binding 'Math.Sin(A*2)-5'}">
CalcBinding 识别出此表达式具有反转表达式 'A = Math.Asin(TextBox.Content + 2) / 2',并将此表达式用于将依赖项属性 TextBox.Text 转换为 ViewModel 的属性 A(当 TextBox 的文本发生更改时)。
前面的表达式等价于以下常用代码:
<TextBox Text = "{Binding Path=A, Conveter={x:StaticResource MyMathConverter}">
public class MyMathConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var source = (int)value;
return Math.Sin(source*2)-5;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var res = Double.Parse(value);
return (int)(Math.Asin(res + 5) / 2);
}
}
-
绑定必须仅包含一个属性路径(静态或非静态)和它的一个条目
-
Binding 只能包含以下运算符和方法:
"+", "- (binary)", "*", "/", "Math.Sin", "Math.Cos", "Math.Tan", "Math.Asin",
"Math.Acos", "Math.Atan","Math.Pow", "Math.Log", "!", "- (unary)"};
CalcBinding 识别具有 Visibility 类型的依赖属性是否绑定到 bool 表达式。如果为 true,则 bool 表达式会自动转换为 Visibility。
Obsiously true 表达式结果转换为 Visibility.Visible
CalcBinding 的属性 FalseToVisibility 指定转换 false 表达式结果的状态。Flag 可以具有以下值之一:
- FalseToVisibility.Collapsed (默认)
- FalseToFisibility.隐藏
<Button Content="TargetButton" Visibility="{c:Binding HasPrivileges, FalseToVisibility=Collapsed}"/>
or just
<Button Content="TargetButton" Visibility="{c:Binding !HasPrivileges}"/>
<Button Content="TargetButton" Visibility="{c:Binding !HasPrivileges, FalseToVisibility=Hidden}"/>
自动反转也分发到这个转换中。如果依赖属性等于 Visibility.Visible,则将其转换为 true,否则转换为 false。
Xaml 是基于 xml 语言的标记语言,xml 不支持在属性值中使用双引号符号。Xaml 也不支持双引号,此外它在 Path 值中支持单引号字符时存在问题:在一个表达式中是有效的,在其他表达式中是 no。为了有机会在 Path (\' 或 ' 或 “) 中编写最紧凑和可读的字符串常量CalcBinding 不会区分双引号和单引号 - 默认情况下,所有引号都被视为双引号。例如:
<TextBox Text="{c:Binding (Name + \' \' + Surname)}" />
<TextBox Text="{c:Binding (IsMan?\'Mr\':\'Ms\') + \' \' + Surname + \' \' + Name}"/>
但是,在这种情况下,我们失去了支持 Char 常量的能力。因此,从版本 2.3 开始,CalcBinding 具有新属性 - SingleQuotes。如果 property 为 true,则 CalcBinding 认为所有引号(双引号和单引号)都是单引号。所以 \'A\' 和 ”A”是该模式下的 Char 符号。如果 property 为 false,则单引号和双引号被视为双引号,默认情况下它是 variant 的。所以 \'A\' 和 ”A”是该模式下的 String 常量。支持 Char 的示例:
<TextBox Text="{c:Binding 'Symbol == "S"?4:5', SingleQuotes=True}"/> {can't use no \' nor ' symbols because of xaml compiler generates error when parses == operator}
其中 Symbol - Char 属性。
- 此版本不支持同时使用 Char 和 String 常量。
虽然 CalcBinding 还没有模拟 TemplateBinding,作为临时解决方案,你可以这样写:
<Button Content="Button" Width="100">
<Button.Template>
<ControlTemplate>
<TextBox Width="{c:Binding Width+10, RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
</Button.Template>
</Button>
将 RelativeSource 属性设置为 TemplatedParent 值会使 CalcBinding 类似于 TemplateBinding
由于某些场景下有大量的 trace 消息,所有 calcbinding 跟踪默认是禁用的(参见 bug 44)。
要启用跟踪,您需要指定最低跟踪级别。将此代码添加到您的 app.config 文件中,以查看所有信息或更高优先级的日志:
<system.diagnostics>
<switches>
<add name="CalcBindingTraceLevel" value="Information"/>
</switches>
</system.diagnostics>
其他可用的跟踪级别:
- 都
- 关闭、
- 危急
- 错误
- 警告
- 信息
- 详细
有关更多信息,请转到 msdn:SourceSwitch
-
反向绑定不支持可为 null 的值类型(例如模式 OneWayToSource)
-
CalcBinding 现在根本不支持您的自定义 conveters。如果你需要这个功能,创建新的问题,把你的使用场景整理好,我可以看到这是必要的
-
在路径表达式中,除了 Math 类之外,不能使用 .Net 类的任何方法。
CalcBinding 使用 DynamicExpresso 库将字符串表达式解析为 Linq Expression 和编译好的表达式树进行绑定。 DynamicExpresso 实际上是 DynamicLinq 库的一个分支,与 DynamicLinq 相比具有许多优点和错误修复(例如,浮点解析取决于 CurrentCulture 该死的错误)。
字符串表达式仅在初始化绑定时解析一次。在 init 部分中,CalcBinding 分析器在 path expression 中查找标记:property path、static property path、Math expression 和 Enum expression。当第一次触发 bind 时,特殊的 binding converter 将每个属性路径和静态属性路径替换为适当类型的变量,并调用 DynamicExpresso 将表达式编译成接受新变量的 delegate。
与每次解析字符串表达式相比,使用编译的表达式可以提高绑定速度。在开发机器上,每次解析的时间是 0.03 秒,使用编译后的表达式的时间是 0.001-0.003 秒
- 枚举常量直接用于 Dynamic Expresso 的表达式,具有已知枚举类型的集合。
- 集合(ListView、ListBox、DataGrid 等)的绑定是在 XAML 中声明的次数的倍数。例如,如果你有 10000 个元素的 ListView,并且每个元素都有由 5 个控件组成的模板,这些控件都已绑定,则只会创建 5 个 Binding 实例。
- 如果一个或多个属性路径更改了结果属性的类型,则重新编译表达式。
1. I wrote logical expression A && B, A < B, A <= B, but my xaml doesn't compile, what's wrong?
由于 Xaml 通常是 xml 格式,因此某些符号被拒绝,应该使用 it's aliases 而不是 its 的别名。请参阅 源属性和运算符 部分中的 operators aliases 表
2. I wrote string expression A + " some text", but my xaml doesn't compile, what's wrong?
在标记扩展中,我们不能使用双引号,因此我们可以使用单引号和反斜杠来转义 \' 或 xml 转义符号 “。参见 String, Char 和 SingleQuotes 模式部分
3. Can I use CalcBinding instead of TemplateBinding?
是的,你可以,但是通过设置 RelativeSource 属性,请参阅 TemplateBinding 部分
浙公网安备 33010602011771号