在WPF中将Image.Source绑定到字符串?

9

我有以下的XAML代码:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    WindowStartupLocation="CenterScreen"
    Title="Window1" Height="300" Width="300">

    <Grid>
        <Image x:Name="TestImage" Source="{Binding Path=ImageSource}" />
    </Grid>

</Window>

此外,还有一种方法可以从Base64字符串创建一个图像:

Image Base64StringToImage(string base64ImageString)
{
    try
    {
        byte[] b;
        b = Convert.FromBase64String(base64ImageString);
        MemoryStream ms = new System.IO.MemoryStream(b);
        System.Drawing.Image img = System.Drawing.Image.FromStream(ms);

        //////////////////////////////////////////////
        //convert System.Drawing.Image to WPF image
        System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(img);
        IntPtr hBitmap = bmp.GetHbitmap();
        System.Windows.Media.ImageSource imageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

        Image wpfImage = new Image();
        wpfImage.Source = imageSource;
        wpfImage.Width = wpfImage.Height = 16;
        //////////////////////////////////////////////

        return wpfImage;
    }
    catch
    {
        Image img1 = new Image();
        img1.Source = new BitmapImage(new Uri(@"/passwordManager;component/images/TreeView/empty-bookmark.png", UriKind.Relative));
        img1.Width = img1.Height = 16;
        return img1;
    }
} 

现在,我将把TestImage绑定到Base64StringToImage方法的输出。
我使用了以下方式:
public string ImageSource { get; set; }
ImageSource = Base64StringToImage("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAABjUExURXK45////6fT8PX6/bTZ8onE643F7Pf7/pDH7PP5/dns+b7e9MPh9Xq86NHo947G7Hm76NTp+PL4/bHY8ojD67rc85bK7b3e9MTh9dLo97vd8/D3/Hy96Xe76Nfr+H+/6f///1bvXooAAAAhdFJOU///////////////////////////////////////////AJ/B0CEAAACHSURBVHjaXI/ZFoMgEEMzLCqg1q37Yv//KxvAlh7zMuQeyAS8d8I2z8PT/AMDShWQfCYJHL0FmlcXSQTGi7NNLSMwR2BQaXE1IfAguPFx5UQmeqwEHSfviz7w0BIMyU86khBDZ8DLfWHOGPJahe66MKe/fIupXKst1VXxW/VgT/3utz99BBgA4P0So6hyl+QAAAAASUVORK5CYIII").Source.ToString(); 

但是什么也没发生。
我该如何修复它?

顺便提一下,我非常确定base64字符串是正确的。


不是与您的问题相关,但您是否尝试使用var img = new BitmapImage { StreamSource = ms }加载图像? - Simon Buchan
@Simon:没有,但是,我为什么要这样做呢? - Mohammad Dayyan
3
你可以直接使用WPF加载图像,而不是通过GDI+(System.Drawing)。因为它是用于XAML的,所以你实际上需要这样做:var source = new BitmapImage(); source.BeginInit(); source.StreamSource = ms; source.EndInit()。请注意,这里的重点是直接使用WPF加载图像,并提供了一个代码示例。 - Simon Buchan
我注意到 ImageSource="resources\refresh.png" 不起作用,但 ImageSource="\resources\refresh.png" 起作用了,其中 ImageSource 是一个依赖属性。而在 Image 控件中直接使用 Source="resources\refresh.png" 是可以工作的。 - Gerard
2个回答

24

让我们分解一下你正在做的事情。

<Image Source="{Binding ImageSource}" />

为了使这个工作正常运行,绑定源必须是一个ImageSource,或者是表示图像文件URI的字符串。因此,让我们看一下ImageSource属性实际上是什么。

public string ImageSource { get; set; }

这里的一个问题是ImageSource没有触发PropertyChanged事件。因此,当您更新属性时,WPF不会更新绑定目标。

但是,另一个问题是ImageSource不是一个ImageSource,而是一个字符串。虽然这没关系,但WPF将把该字符串解释为URI。那么这个URI是什么?

ImageSource = Base64StringToImage(BIG_HONKING_STRING).Source.ToString(); 
这是问题的关键。ImageSource字符串实际上不是URI,因为您的图像不是可寻址的资源。Base64StringToImage从base64字符串创建一个内存中的ImageSource,然后返回一个以此作为源的Image。然后您取出Image的Source(它是一个ImageSource对象),并将其转换成字符串。如果ImageSource来自文件或URL,则可能会起作用,但事实并非如此:它来自HBITMAP。因此,ToString()方法的结果将是无意义的。因此,ImageSource被设置为某个无意义的内容,而您的Image正在尝试将此无意义的内容解释为位图文件的URL。
要解决这个问题,您需要做三件事:
1. 为ImageSource属性引发PropertyChanged事件(或将其设置为依赖属性)。 2. 将ImageSource属性的类型更改为ImageSource,而不是字符串类型(以便可以包含无URL的图像源)。 3. 更改您的setter调用,将ImageSource设置为Base64StringToImage(...) .Source — 也就是删除ToString()调用。最好的方法是,将Base64StringToImage更改为返回ImageSource而不是Image:创建Image元素只会创建开销,因为您真正感兴趣的只是BitmapSource。

Image.Source can also be bound to a Stream or byte[] - Jack Ukleja

17

作为对@itowlson出色答案的补充,以下是您代码应该的样子:

// MainWindow.xaml
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <Image Source="{Binding ImageSource}" />
    </DockPanel>
</Window>

// MainWindow.xaml.cs
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var model = new MainModel();
        DataContext = model;

        model.SetImageData(File.ReadAllBytes(@"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"));
    }
}

class MainModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void SetImageData(byte[] data) {
        var source = new BitmapImage();
        source.BeginInit();
        source.StreamSource = new MemoryStream(data);
        source.EndInit();

        // use public setter
        ImageSource = source;
    }

    ImageSource imageSource;
    public ImageSource ImageSource
    {
        get { return imageSource; }
        set
        {
            imageSource = value;
            OnPropertyChanged("ImageSource");
        }
    }

    protected void OnPropertyChanged(string name)
    {
        var handler = PropertyChanged;
        if (null != handler)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

谢谢,我已经完成了,但是没有显示任何内容。只有一个字符串显示,而不是图像 => WpfApplication1.MainModel。你可以从http://www.mediafire.com/?nze3qjmzjtm下载我的项目。 - Mohammad Dayyan
几乎是这样的 DataContext = model;,而不是 Content = model;。此外,在 SetImageData() 中可以直接使用 Convert.FromBase64String(...)BitmapImage 可以加载与 System.Drawing.Image 相同的图像。 - Simon Buchan
非常感谢你,兄弟,我真的需要它。谢谢 :-) - Mohammad Dayyan

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