使用XAML将System.Drawing.Image绑定到System.Windows.Image控件中

18

我正在将一个ListView绑定到一个对象列表,就像这样:

<ListView 
    ItemsSource="{ Binding Path=. }"
    ItemTemplate="{DynamicResource EventTemplate}">   

我正在绑定到一个声明了两个属性的对象;

string DisplayName { get; }
System.Drawing.Image Image { get; set; }

我想填充一个 DataTemplate,但我不知道该如何操作;如果我在我的模板中这样做;
<StackPanel Orientation="Horizontal">
    <Image Source="{ Binding Path=Image }" />
    <TextBlock Text="{ Binding Path=DisplayName }" />
</StackPanel>      

文本显示出来了,但是图片没有显示。我做错了什么?调试输出显示
System.Windows.Data Error: 1 : Cannot create default converter
to perform 'one-way' conversions between types
'System.Drawing.Image' and 'System.Windows.Media.ImageSource'.
Consider using Converter property of Binding.
BindingExpression:Path=Image; DataItem='RealElement'
(HashCode=54308798); target element is 'Image' (Name='');
target property is 'Source' (type 'ImageSource')
2个回答

40

我找到了一种自己满意的方法。使用Reed Copsey的指针这个教程,我将代码包装成了IValueConverter

以下是从System.Drawing.ImageSystem.Windows.Media.ImageSource的转换器;

using System;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Windows.Data;

namespace System.Windows.Media
{
    /// <summary>
    /// One-way converter from System.Drawing.Image to System.Windows.Media.ImageSource
    /// </summary>
    [ValueConversion(typeof(System.Drawing.Image), typeof(System.Windows.Media.ImageSource))]
    public class ImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
            object parameter, CultureInfo culture)
        {
            // empty images are empty...
            if (value == null) { return null; }

            var image = (System.Drawing.Image)value;
            // Winforms Image we want to get the WPF Image from...
            var bitmap = new System.Windows.Media.Imaging.BitmapImage();
            bitmap.BeginInit();
            MemoryStream memoryStream = new MemoryStream();
            // Save to a memory stream...
            image.Save(memoryStream, ImageFormat.Bmp);
            // Rewind the stream...
            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
            bitmap.StreamSource = memoryStream;
            bitmap.EndInit();
            return bitmap;
        }

        public object ConvertBack(object value, Type targetType,
            object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

那么您需要将图像转换器作为资源引入到XAML中;

xmlns:med="clr-namespace:System.Windows.Media"
...

<ListView.Resources>
    <med:ImageConverter x:Key="imageConverter" />
</ListView.Resources>

然后,您可以在XAML中使用新转换器将其直接绑定到图像;

<Image Source="{ Binding Path=Image, Converter={StaticResource imageConverter} }" />

1
我有一个关于内存使用的问题:这个转换只发生一次还是每次图像在可见部分,例如ListBox中显示时都会进行转换? - emesx
1
很难说——WPF每次需要转换时都会执行转换,所以我认为它只是一个内部实现细节。我不认为有任何理由反复进行转换,但如果值得的话,可以尝试增加一个计数器来观察。 - Steve Cooper
3
memoryStream没有被关闭或释放,这会有影响吗?我有一些非常相似的代码,可能甚至来自于这个问题,但它很丑陋。 - Maslow
2
对于未来的读者,请记得在属性设置器中处理前一个“Image”实例。这是针对@Maslow评论的回应。 - Noctis Tong

5

你不能直接绑定它 - 你需要有某种类型的转换器,将GDI图像转换为WPF图像。

这里是一种方法 - 它使用内存流从GDI图像中提取数据并创建一个BitmapSource对象。


嗨 - 谢谢你的指引。我正在阅读你的“从Windows Forms到WPF与MVVM的更好的用户和开发者体验”文章,目前已经看了一半;非常棒的内容。无论如何,这让我有足够的信息来得出答案;我已经发布在旁边了。 - Steve Cooper
@Steve:很高兴听到你喜欢这个系列 ;) - Reed Copsey
这真的很有帮助。WPF模型与Windows Forms非常不同,您的文章正在帮助我理解它。 - Steve Cooper
在那个例子中,memoryStream 从未被释放,也没有使用 using 或关闭。这是一个问题吗? - Maslow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接