views:

718

answers:

2

I have below XAML code :

<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>

Also, there is a method that makes an Image from a Base64 string :

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;
    }
} 

Now, I'm gonna bind TestImage to the output of Base64StringToImage method.
I've used the following way :

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(); 

but nothing happen.
How can I fix it ?

BTW, I'm dead sure that the base64 string is correct

+3  A: 

Let's break down what you're doing.

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

For this to work, the binding source needs to be either an ImageSource, or a string representing a URI to an image file. So let's take a look at what the ImageSource property actually is.

public string ImageSource { get; set; }

One problem here is that ImageSource isn't raising PropertyChanged events. So WPF won't update the binding target when you update the property.

But also, ImageSource isn't an ImageSource, it's a string. That's okay, but WPF is going to interpret that string as a URI. What is that URI?

ImageSource = Base64StringToImage(BIG_HONKING_STRING).Source.ToString(); 

This is the nub of your problem. The ImageSource string isn't actually a URI, because your image isn't an addressable resource. Base64StringToImage creates an in-memory ImageSource from the base64 string, then return an Image with that as its Source. Then you take the Image's Source (which is an ImageSource object), and stringise that. That might work if the ImageSource had come from a file or URL, but it didn't: it came from a HBITMAP. So the result of ToString() is going to be meaningless. So ImageSource is being set to something meaningless, and your Image is trying to interpret this meaningless thing as the URL of a bitmap file.

So to fix this you need to do three things:

  1. Raise the PropertyChanged event for the ImageSource property (or make it a dependency property).
  2. Change the ImageSource property to be of type ImageSource instead of type string (so that it can contain URL-less image sources).
  3. Change your setter call to set ImageSource to Base64StringToImage(...).Source -- i.e. remove the ToString() call. Better still, change Base64StringToImage to return an ImageSource rather than an Image: creating an Image element just creates overhead because all you're really interested in is the BitmapSource.
itowlson
+1  A: 

As a complement to @itowlson's excellent answer, this is what your code should look like:

// 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));
        }
    }
}
Simon Buchan
Thanks, I've done it, but nothing has showed. Just a string showed instead of image => `WpfApplication1.MainModel`. you can download my project from http://www.mediafire.com/?nze3qjmzjtm
Mohammad
Nearly, it's `DataContext = model;`, not `Content = model;`. Also, you can use `Convert.FromBase64String(...)` directly in `SetImageData()`, `BitmapImage` can load the same images as `System.Drawing.Image`.
Simon Buchan
@Simon: Thank you very much bro, I really needed it. Thanks :-)
Mohammad