类型不匹配的数据绑定
- 10 分钟
有时,你使用的数据与显示数据的控件属性的数据类型不匹配。 例如,你可能有一个货币值存储在要显示在 decimal 控件上的 Label 类型中,其格式为货币。 一个更复杂的示例是模块中提供的天气应用。 图像应根据天气预报的 Sunny 或 Cloudy 枚举值显示,但你无法将源的枚举值绑定到目标的图像属性。 本单元着眼于转换数据的方法。
字符串格式设置
常见的类型不匹配是想要显示为格式字符串的内部类型。 就像要显示 DateTime 值的一部分,或者想要将 decimal 类型格式化为货币时一样。
假设你想要显示账单上的到期金额,并且你的数据对象上有此属性:
public decimal BillAmount { get; set; }
欠款金额最终为 22.0304。 可以使用两个标签控件来显示一些文本和美元金额,如以下代码片段所示:
<HorizontalStackLayout>
<Label Text="You owe" Margin="0,0,5,0" />
<Label Text="{Binding BillAmount}" />
<Label Text="to the bank" Margin="5,0,0,0" />
</HorizontalStackLayout>
这会向 UI 输出一个类似于 You owe 22.0304 to the bank 的字符串,但它缺少货币符号,并且根据本地货币,小数位数可能过多或过少。 通常,你将在代码中将值处理为带有“C”(或货币)格式说明符的字符串,如下所示:
string formattedBillAmount = string.Format("{0:C}", BillAmount);
但是,若要在数据绑定中使用格式设置,必须让数据对象提供格式化字符串作为其他属性,或者以某种方式截获它并自行设置格式。 幸运的是,.NET MAUI 绑定提供了一种使用 StringFormat 绑定属性格式化字符串的方法。 格式字符串遵循与 String.Format 方法相同的规则。 使用单引号将格式字符串括起来,这样 XAML 分析器就不会因为大括号而混淆了。 字符串格式参数 0 是绑定处理的属性值。
<Label Text="{Binding BillAmount, StringFormat='You owe {0:C} to the bank'}" />
请考虑以下 XAML,它演示了显示 BillAmount 的两种方式:
<VerticalStackLayout Padding="10">
<HorizontalStackLayout>
<Label Text="You owe" Margin="0,0,5,0" />
<Label Text="{Binding BillAmount}" />
<Label Text="to the bank" Margin="5,0,0,0" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="{Binding BillAmount, StringFormat='You owe {0:C} to the bank'}" />
</HorizontalStackLayout>
</VerticalStackLayout>
下图说明了 XAML 输出在屏幕上生成的内容:
使用 StringFormat 绑定属性的 XAML 比其他 XAML 更简单,并且你有权访问 .NET 强大的字符串格式化系统。
自定义类型转换
当将值显示为字符串时,StringFormat 绑定属性很方便,但当你想要从其他类型转换 Color 或 Image 等内容时,则不太方便。 在这些情况下,需要编写自定义转换代码。
假设你提示用户选择密码,并且你想要在 UI 中使用颜色来指示密码强度。 强度分为三个等级:弱、良、强。 强度取决于密码中包含的字符数。 为了向用户提供有关其密码强度的即时反馈,需要包含密码的 Entry 控件的背景根据强度进行更改。 下图展示了这三个强度级别:弱、良和强。
在屏幕截图中的三个输入控件中,第一个输入了四个字符并具有红色背景。 第二个输入了九个字符,并具有黄色背景。 最后一个输入控件有 15 个字符,具有蓝色背景。
这些级别分配给 Strength 枚举:
private enum Strength
{
Weak,
Good,
Strong
}
数据对象被分配为页面的 BindingContext,其中包含带有 PasswordStrength 属性的密码强度。 当用户输入密码时,PasswordStrength 属性会根据密码的长度进行更改。 由于数据对象包含 PasswordStrength 属性,因此将该属性绑定到 BackgroundColor 控件的 Entry:
<Entry BackgroundColor="{Binding PasswordStrength} ... />
不过,这里有问题。
PasswordStrength 的类型为 Strength,而 BackgroundColor 的类型为 Color。 这些类型彼此不兼容。 .NET MAUI 提供了一种解决这些类型不匹配问题的方法,即绑定的 Converter 属性。
绑定转换器,顾名思义,就是在绑定源和目标之间进行转换。 转换器通过 IValueConverter 接口实施。
实现 IValueConverter
你将转换逻辑创建到实施 IValueConverter 接口的类中。 通常,这些类的名称以名称 Converter 结尾,以明确其用途。
IValueConverter 接口定义两种方法:
Convert:从绑定源的属性转换为绑定目标的 UI 属性。ConvertBack:从绑定目标的 UI 属性转换回绑定源的属性。这种方法很少使用,在这种情况下也不会使用。 大多数转换器会抛出
NotSupportedException来指示不支持此转换。
下面是接口的协定:
public interface IValueConverter
{
object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture);
object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture);
}
回想一下,我们正在处理的方案是将 Entry.BackgroundColor 属性绑定到数据对象的 PasswordStrength 属性,这是一个 Strength 枚举。
<Entry BackgroundColor="{Binding PasswordStrength} ... />
Strength枚举需要转换为 aColor,因此转换器需要评估提供的值Strength并返回不同的Color值。 下面的代码演示了这种转换:
namespace MyProject.Converters;
class StrengthToColorConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return (Strength)value! switch
{
Strength.Weak => Colors.OrangeRed,
Strength.Good => Colors.Yellow,
Strength.Strong => Colors.LightBlue,
_ => Colors.LightBlue
};
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
throw new NotImplementedException();
}
让我们分解以下代码:
-
Convert方法有四个参数。 通常可以放弃最后两个参数,除非出于特定原因需要使用它们。 -
value参数包含传入值。 在此示例中,它是一个Strength枚举值。 - 忽略
targetType该参数,但可以使用此参数来验证转换器正在使用的属性类型是否为 。Color为了简单起见,本示例中省略了这一点。 - switch 表达式根据
Strength值返回不同的颜色。
在 XAML 中使用转换器
创建转换器类后,需要创建它的实例并在绑定中引用它。 实例化转换器的标准方法是在根元素的资源字典中。
首先,将 XML 命名空间映射到包含转换器的 .NET 命名空间。 在以下代码示例中,cvt XML 命名空间映射到 MyProject.Converters .NET 命名空间:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cvt="clr-namespace:MyProject.Converters"
...
接下来,在 ContentPage.Resources 中创建一个实例:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cvt="clr-namespace:MyProject.Converters"
x:Class="MyProject.PasswordExample">
<ContentPage.Resources>
<cvt:StrengthToColorConverter x:Key="StrengthToColorConverter" />
</ContentPage.Resources>
现在,转换器的实例位于具有键的资源字典中,该键 StrengthToColorConverter恰好与类型同名。 这是命名转换器的典型方式,因为你通常只有一个在整个 XAML 中重复使用的转换器。 如果出于某种原因需要多个转换器实例,则它们之间的密钥必须不同。
最后,在绑定时引用转换器。 由于转换器位于资源字典中,因此可以使用 {StaticResource} 标记扩展来引用它。 该转换器被分配给 Converter 绑定属性:
<Entry BackgroundColor="{Binding PasswordStrength, Converter={StaticResource StrengthToColorConverter}}" ... />