在 WPF 中使用 MarkupExtension 和 Converter
使用 MarkupExtension 优化 WPF 中的转换器
介绍
软件需要用与存储值不同的值来表示显示组件。WPF 提供了 Converter
(IValueConverter
/IMultiValueConverter
) 功能来轻松地进行这种转换。我们将看到如何使用 converter
以及使用 MarkupExtension
访问 converter
的最佳方式。
背景
Markup Extension 是 WPF 框架提供的用于扩展 XAML 的内置功能。Markup extension 定义在花括号 ({}
) 内。
WPF 框架提供的几个内置扩展包括
BindingExtension
StaticResourceExtension
DynamicResourceExtension
RelativeSourceExtension
StaticExtension
TypeExtension
就像 Dependency property 或属性一样,我们在 XAML 中访问它时不需要写“extension”这个词。
Using the Code
本文主要分为两部分,如下所示
- 在 WPF 中使用 Converter
- 使用
MarkupExtension
来优化Converter
的构建
注意: 以下示例仅用于演示该概念。
在 WPF 应用程序中使用 Converter
Converter 在 WPF 中用于将一种数据类型的值转换为另一种数据类型。或者用简单的话来说,Converter
(继承自 IValueConverter
的类)接受一种格式的值并以另一种格式返回值。
public class NumberToStringConverter : IValueConverter
{
}
为了访问这个 converter
,我们在 XAML 中声明一个资源。例如,
<Window.Resources>
<local:NumberToStringConverter x:Key="numConverter"></local:NumberToStringConverter>
</Window.Resources>
然后,我们按如下方式在 Binding
中访问这个 converter
:
<TextBox x:Name="txtBox1">100</TextBox>
<TextBox Text="{Binding Path=Text,Converter=
{StaticResource numConverter},ElementName=txtBox1}" >
这是一个演示 converter
用法的简单示例。如果我们在不同的 XAML 中需要 converter
,我们需要在每个 XAML 中重复上述代码。
现在,如果你仔细观察,这是一种冗余编码。我们将最终得到 n 个 converter
实例。你不认为 converter
的作用是将一种类型的值转换为另一种类型的值吗?那么我们真的需要创建 n 个 converter
类的实例吗?
使用 MarkupExtension 和 Singleton 创建 Converter
我们可以利用 WPF 的内置功能来消除这种冗余。WPF 提供了 MarkupExtension
在 XAML 中访问您的自定义对象。WPF MarkupExtension
允许我们以一种优化方式编写转换器。让我们看看如何做。
在上述情况下,我们可以在 XAML 中访问转换器实例,而无需创建其资源实例。为了实现这一点,Converter
必须从 MarkupExtension
继承。MarkupExtension
是 WPF 框架定义的 abstract
类。我们需要重写 ProvideValue
方法。ProvideValue
方法接受类型为 IServiceProvider
(也是一个框架声明的接口)的变量。我们可以使用此方法返回 Converter
的单例实例,如下所示:
[MarkupExtensionReturnType(typeof(IValueConverter))]
public class NumberToStringConverterExtension: MarkupExtension, IValueConverter
{
private static NumberToStringConverterExtension _converter;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
{
_converter = new NumberToStringConverterExtension();
}
return _converter;
}
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return Binding.DoNothing;
}
return GetString(value);
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && !string.IsNullOrEmpty(value.ToString()))
{
return GetInt(value);
}
return 0;
}
#endregion
private string GetString(object value)
{
//Some logic to convert int to string..
return value.ToString();
}
private int GetInt(object value)
{
//Some logic to convert string to int..
int val;
int.TryParse(value.ToString(), out val);
return val;
}
}
我们可以在 XAML 中按如下方式使用此单例实例
<TextBox x:Name="txtBox1">100
<TextBox Text="{Binding Path=Text,Converter=
{local:NumberToStringConverter},ElementName=txtBox1}">
注意:在上面的代码片段中,您可以看到 NumberToStringConverter
扩展对象是在没有编写 Extension
后缀的情况下访问的。
值得关注的点
使用 MarkupExtension
继承转换器允许开发人员直接在 XAML 中访问 Converter
,而无需将其声明为资源。同样,由于 converter
用于通用活动,因此实例可以设置为单例,从而限制用户创建多个实例。
历史
- 第一个版本 - 2009 年 4 月 14 日 - 解释了如何使用
MarkupExtension
将Converter
作为 WPF 扩展来访问