Search code examples
wpfxamlxpsbitmapsource

All images in XPS look identical when loaded from a byte array


I have a window:

<Window x:Class="WpfTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="150" Height="100"
        MouseLeftButtonDown="OnClick">
    <StackPanel Orientation="Horizontal">
        <Image Source="{Binding Image1}" />
        <Image Source="{Binding Image2}" />
    </StackPanel>
</Window>

codebehind:

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new
        {
            Image1 = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAKhJREFUWIXtltEJgzAURU+l7Qg6S+kiHaOLdBL9EgKlEzhTUrA/fkgM+Ah5FeUdCAmSvHvUEAKGsaQBHOCBsVALU81GIuAKBsfNxWGnhIAHLhLTDAJwXRMYlcKTmZVy2CrnzHUvYIie3YHnvwQGoCtRa7e/oJ2NUxtZzOZfwARMwARMIPcknPOY+lvOYrsPpAS+inlBIvBRFHhLJmlcyz3QA3WxVzEOww83D06TNQuS8AAAAABJRU5ErkJggg=="),
            Image2 = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAPFJREFUWIXtlksKwjAURY9iBQcORLArcOAanLsChQ7dggtwU0JBBKduxIF7aAt1YJy0iUnz8UcuPGhKbt55r2kIREW1lQI5UAC1pyjFmqkJQO4xcTPyZrKeBKAAEhNSC5XAUAdQB0ouzdkPnEyrgYVno1hnBWyRd7WTdBvplfaOfmeAyTsAZLG2LeAnNyHADbgAlRhf/eA8pGvhERgpvAsDvzPAXOEbA+euADYn4dOzBHbiORHjqcarytkCMKkgM5j7/X+ByydQqZP/4x2wOQcynwDxPiADqCTvfKk0ATgFBDiaTApxLS+AAzDzVkrU3+gOjt+/W2ggWToAAAAASUVORK5CYII="),
        };
    }

    private void OnClick(object sender, MouseButtonEventArgs e)
    {
        using (var xps = new XpsDocument(@"1.xps", FileAccess.Write))
            XpsDocument.CreateXpsDocumentWriter(xps).Write((Visual)this.Content);
    }
}

As you can see, it displays two images and writes itself to an XPS file on mouse click.

This is what I see on the screen:

This is what I get in the XPS:

So the first image is displayed instead of the second one. In fact, if I have multiple images, all of them are replaced with the first one in the resulting XPS.

However, if instead of byte[] I set image's source to a file URL, then XPS correctly displays all the images.

What is going on?


Solution

  • Investigation shows, that when Image.Source is assigned to a byte array, ImageSourceConverter class is used by WPF to convert the array to the proper ImageSource. BitmapFrame.Create() is called inside to perform the actual bitmap loading. So this code have absolutely the same effect:

    var view = new StackPanel
    {
        Orientation = Orientation.Horizontal,
        Children =
        {
            new Image { Source = BitmapFrame.Create(new MemoryStream(pic1Bytes), BitmapCreateOptions.None, BitmapCacheOption.Default) },
            new Image { Source = BitmapFrame.Create(new MemoryStream(pic2Bytes), BitmapCreateOptions.None, BitmapCacheOption.Default) },
        }
    };
    using (var xps = new XpsDocument(@"1.xps", FileAccess.Write))
        XpsDocument.CreateXpsDocumentWriter(xps).Write(view);
    

    However, if I load image using BitmapImage class, the problem no longer exists. This code works both on screen and in XPS:

    private static BitmapSource ImageFromBytes(byte[] bytes)
    {
        var bmp = new BitmapImage();
        using (var stream = new MemoryStream(bytes))
        {
            bmp.BeginInit();
            bmp.CacheOption = BitmapCacheOption.OnLoad;
            bmp.StreamSource = stream;
            bmp.EndInit();
        }
        return bmp;
    }
    
    // usage example:
    new Image { Source = ImageFromBytes(picBytes) }
    

    This is probably a bug in ImageSourceConverter. One could create a custom converter using ImageFromBytes inside to continue using XAML binding.