图像控件演示XAML的一个有趣属性,称为类型转换。例如,对于图像的XAML API是这样的:
<Image Source="http://lorempixel.com/100/100/people" />
然而,编程API是这样的:
class Image {
ImageSource Source { get; set;}
DependencyProperty SourceProperty // etc.
}
怎么字符串得到变成了一个开放的,然后变成了一个ImageSource的?
答案在于TypeConverters。
[TypeConverter(typeof(ImageSourceConverter))]
public class ImageSource {}
当我们以编程方式创建一个绑定到Uri,上面的魔术不会发生。结果是没有图片显示。
// No picture is shown.
BindingOperations.SetBinding(myImage,
Image.SourceProperty, new Binding("MyUri"));
同样,我们不能做到这一点:
// compile time error
myImage.Source = new Uri("http://...")
相反,正确的方法是取从ImageSource的的自定义属性的类型转换器和按摩到一个的IValueConverter。下面是我的 - 主要工作是通过这一条线上public object Convert(...)
执行 - 一切是脚手架:
namespace Caliburn.Micro
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
public class ValueTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var result = TypeDescriptor.GetConverter(targetType).ConvertFrom(value);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
/// <summary>
/// Binding Image.Source to an Uri typically fails.
/// Calling the following during application bootstrap will set this up properly.
/// ConventionManager.ApplyValueConverter = ValueTypeConverter.ApplyValueConverter;
/// </summary>
/// <param name="binding"></param>
/// <param name="bindableProperty"></param>
/// <param name="info"></param>
public static void ApplyValueConverter(Binding binding, DependencyProperty bindableProperty, PropertyInfo info)
{
if (bindableProperty == UIElement.VisibilityProperty && typeof(bool).IsAssignableFrom(info.PropertyType))
binding.Converter = ConventionManager.BooleanToVisibilityConverter;
else if (bindableProperty == Image.SourceProperty && typeof(Uri).IsAssignableFrom(info.PropertyType))
binding.Converter = new ValueTypeConverter();
else
{
foreach (var item in _Conventions)
{
if (bindableProperty == item.Item1 && item.Item2.IsAssignableFrom(info.PropertyType))
binding.Converter = new ValueTypeConverter();
}
}
}
/// <summary>
/// If there is a TypeConverter that can convert a <paramref name="SourceType"/>
/// to the type on <paramref name="bindableProperty"/>, then this has to
/// be manually registered with Caliburn.Micro as Silverlight is unable to
/// extract sufficient TypeConverter information from a dependency property
/// on its own.
/// </summary>
/// <example>
/// ValueTypeConverter.AddTypeConverter<ImageSource>(Image.SourceProperty);
/// </example>
/// <typeparam name="SourceType"></typeparam>
/// <param name="bindableProperty"></param>
public static void AddTypeConverter<SourceType>(DependencyProperty bindableProperty)
{
_Conventions.Add(Tuple.Create<DependencyProperty, Type>(bindableProperty, typeof(SourceType)));
}
private static IList<Tuple<DependencyProperty, Type>> _Conventions = new List<Tuple<DependencyProperty, Type>>();
}
}
然后在引导程序,我们连线了新的IValueConverter:
protected override void Configure()
{
// ...
ConventionManager.ApplyValueConverter =
ValueTypeConverter.ApplyValueConverter;
}
Caliburn不允许简单地写' '?对我来说看起来好多了。 –
Clemens
请注意*强制*表示不同的意思。你的意思是所谓的类型转换。 – Clemens
你说的都对。谢谢。我实际上正在研究使用类型转换来显示可以被用户替换的图像,因为它似乎是隐藏大部分相关复杂性的好方法。 –