2013-12-15 129 views
1

为什么以下图像无法正确绑定到源?使用Caliburn.Micro将图像绑定到Uri

<UserControl x:Class="SlCaliburnConventionTest.Sample" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" 
    d:DesignHeight="300" d:DesignWidth="400"> 

    <Grid x:Name="LayoutRoot" Background="White"> 
     <Image x:Name="UriProperty" /> 
    </Grid> 
</UserControl> 

背后的代码和视图模型:

namespace SlCaliburnConventionTest 
{ 
    using System; 
    using System.Windows.Controls; 

    public partial class Sample : UserControl 
    { 
     public Sample() 
     { 
      InitializeComponent(); 

      var viewModel = new SampleViewModel("http://lorempixel.com/300/200/sports/1"); 
      Caliburn.Micro.ViewModelBinder.Bind(viewModel, this, null); 
     } 
    } 

    public class SampleViewModel 
    { 
     public SampleViewModel(string url) 
     { 
      UriProperty = new Uri(url, UriKind.Absolute); 
     } 

     public Uri UriProperty { get; set; } 
    } 
} 

我挖成Caliburn.Micro源,发现施加的约定,当它被不使用TypeDescriptor。问题是:我们如何说服Caliburn.Micro将Uris转换为ImageSource?

回答

2

图像控件演示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&lt;ImageSource&gt;(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; 
} 
+1

Caliburn不允许简单地写''?对我来说看起来好多了。 – Clemens

+1

请注意*强制*表示不同的意思。你的意思是所谓的类型转换。 – Clemens

+0

你说的都对。谢谢。我实际上正在研究使用类型转换来显示可以被用户替换的图像,因为它似乎是隐藏大部分相关复杂性的好方法。 –

2

我用一个字符串作为支持属性和绑定为我工作:

public class TestViewModel : ViewModelBase 
{ 
    public TestViewModel() 
    { 
     ImageUrl = "http://cdn.sstatic.net/stackoverflow/img/apple-touch-icon.png"; 
    } 

    public string ImageUrl { get; set; } 
} 

<Image Source="{Binding ImageUrl}" /> 
+0

感谢罗布。我特别想了解如何使TypeConverters与Caliburn.Micro一起工作。 –

相关问题