views:

501

answers:

1

I'm trying to understand how image serialization works in WPF. I have the following class:

[Serializable]
public class TestClass : ISerializable
{
    public TestClass() { }


    protected TestClass(SerializationInfo info, StreamingContext context)
    {
        SerializedImage = (byte[])info.GetValue("SerializedImage", typeof(byte[]));
    }


    public byte[] SerializedImage { get; set; }


    public Image Image { get; set; }


    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("SerializedImage", SerializedImage);
    }


    [OnSerializing]
    private void OnSerializing(StreamingContext sc)
    {
        BitmapImage image = Image.Source as BitmapImage;

        MemoryStream stream = new MemoryStream();
        BmpBitmapEncoder encoder = new BmpBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(image));
        encoder.Save(stream);

        SerializedImage = stream.ToArray();

        stream.Close(); ;
    }


    [OnDeserialized]
    private void OnDeserialized(StreamingContext sc)
    {
        MemoryStream stream = new MemoryStream(SerializedImage);
        Image = new Image
        {
            Source = BitmapFrame.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad)
        };

        stream.Close();
    }
}

This is the 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"
Title="Window1" Height="358" Width="300" ResizeMode="NoResize">
<StackPanel>

    <Image x:Name="Screenshot" Height="178" />
    <Button Width="80" Height="30" Content="Load Image" Click="Button_Click_1" />
    <Button Width="92" Height="30" Content="Save to file" Click="Button_Click_2" />
    <Button Width="92" Height="30" Content="Load File" Click="Button_Click_3" />
    <Button Width="92" Height="30" Content="Load File (1)" Click="Button_Click_4" />

</StackPanel>

And this is the code behind:

public partial class Window1 : Window
{
    TestClass test;


    public Window1()
    {
        InitializeComponent();

        test = new TestClass();
    }


    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        OpenFileDialog dialog = new OpenFileDialog { Filter = "Bmp Image | *.bmp" };
        dialog.ShowDialog();

        if (dialog.FileName != string.Empty)
        {
            Screenshot.Source = new BitmapImage(new Uri(dialog.FileName));
            test.Image = Screenshot;
        }
    }


    private void Button_Click_2(object sender, RoutedEventArgs e)
    {
        SaveFileDialog dialog = new SaveFileDialog { DefaultExt = ".t", AddExtension = true };
        dialog.ShowDialog();

        if (dialog.FileName != string.Empty)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = new FileStream(dialog.FileName, FileMode.Create);
            formatter.Serialize(stream, test);
            stream.Close();
        }
    }


    private void Button_Click_3(object sender, RoutedEventArgs e)
    {
        OpenFileDialog dialog = new OpenFileDialog { Filter = "TEST file | *.t" };
        dialog.ShowDialog();

        if (dialog.FileName != string.Empty)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = new FileStream(dialog.FileName, FileMode.Open);
            test = formatter.Deserialize(stream) as TestClass;
            stream.Close();

            Screenshot = test.Image;
        }
    }


    private void Button_Click_4(object sender, RoutedEventArgs e)
    {
        OpenFileDialog dialog = new OpenFileDialog { Filter = "TEST file | *.t" };
        dialog.ShowDialog();

        if (dialog.FileName != string.Empty)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = new FileStream(dialog.FileName, FileMode.Open);
            test = formatter.Deserialize(stream) as TestClass;
            stream.Close();

            Screenshot.Source = null;

            MemoryStream stream1 = new MemoryStream(test.SerializedImage);
            Screenshot.Source = BitmapFrame.Create(stream1, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
        }
    }
}

Now for some reason that I don't know, the deserialization of the image in TestClass is not working well. If I grab the byte array and convert it back to an image myself (Button_Click_4), it works and the image is shown on my form. If, instead, I grab the image directly from the Image property in TestClass, nothing will show on the form. I don't know how this is possible, since the code involved in both situations is the same, unless something else is happening behind the scenes.

What am I doing wrong? I have provided the complete code, you can paste it and run it to see the problem.

+1  A: 

The Image object in your visual tree is referenced by a field called "Screenshot". Assigning this.Screenshot to a new Image does not alter your visual tree at all. It just assigns the field Screenshot. In the case where you set the Source, you are updating the Image that is in your visual tree.

Ideally, your test class should expose an ImageSource property, not an Image. A single Image instance can only appear once in the visual tree. So by exposing an ImageSource property, you can reuse that in several instances of Image (that may be used in various locations in your visual tree).

Tom Goff
Thanks, I realize now that the problem was I was re-assigning the Image control, instead of the image of the control.
Austin