Привязка Image.Source к строке в WPF? - PullRequest
8 голосов
/ 04 апреля 2010

У меня есть код ниже 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 верна

Ответы [ 2 ]

22 голосов
/ 04 апреля 2010

Давайте разберемся, что вы делаете.

<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 создает ImageSource в памяти из строки base64, а затем возвращает Image с этим в качестве источника. Затем вы берете источник изображения (который является объектом ImageSource) и структурируете его. Это может сработать, если ImageSource получен из файла или URL, но это не так: он получен из HBITMAP. Таким образом, результат ToString () будет бессмысленным. Поэтому ImageSource устанавливается на что-то бессмысленное, и ваше изображение пытается интерпретировать эту бессмысленную вещь как URL-адрес файла растрового изображения.

Итак, чтобы исправить это, вам нужно сделать три вещи:

  1. Вызовите событие PropertyChanged для свойства ImageSource (или сделайте его свойством зависимости).
  2. Измените свойство ImageSource на тип ImageSource вместо строки типа (чтобы оно могло содержать источники изображений без URL).
  3. Измените ваш установочный вызов, чтобы установить ImageSource на Base64StringToImage(...).Source - то есть удалите вызов ToString (). Более того, измените Base64StringToImage, чтобы он возвращал ImageSource, а не Image: создание элемента Image просто создает накладные расходы, потому что все, что вас действительно интересует, это BitmapSource.
16 голосов
/ 04 апреля 2010

В дополнение к отличному ответу @ 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));
        }
    }
}
...