2013-12-15 81 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一起工作。 –

相關問題