如何序列化包含BitmapImage的类? [英] How to serialize a Class contains BitmapImage?

查看:140
本文介绍了如何序列化包含BitmapImage的类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个DeepCopy方法,该方法可以序列化传入参数的对象,并返回反序列化的对象以进行深度复制。

I have a DeepCopy method, which serializes the object passed in parameter and returns back the deserialized object to make deep copy.

我的方法是:

public static class GenericCopier<T>
{     
           public static T DeepCopy(object objectToCopy)
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    BinaryFormatter binaryFormatter = new BinaryFormatter();
                    binaryFormatter.Serialize(memoryStream, objectToCopy);
                    memoryStream.Seek(0, SeekOrigin.Begin);
                    return (T)binaryFormatter.Deserialize(memoryStream);
                }
            }
}

如果传递给该参数的对象不包含任何BitmapImage字段和属性。

It works well, if the object passed to the parameter doesn't contain any BitmapImage Field and Properties.

public class MyClass
{
  public string TestString {get; set;}
  public BitmapImage TestImage { get; set;}
}

如果我使用MyClass制作DeepCopy,则

If I make DeepCopy of MyClass,

MyClass orginal = new MyClass(){ TestString = "Test"};
MyClass copy = GenericCopier<MyClass>.DeepCopy(orginal);

抛出异常

程序集中的类型'System.Windows.Media.Imaging.BitmapImage'未标记为可序列化

我找到了一种序列化BitmapImage的方法在这里

I found a method to serialize BitmapImage here

但是,如何混合使用两种类型序列化(BinaryFormatter和PngBitmapEncoder)以序列化MyClass?

But, How can i mix the both type of serialization (BinaryFormatter & PngBitmapEncoder) to serialize MyClass?

推荐答案

您在这里有两个选择:

选项1:实施 ISerializable 并快照到PNG

Option 1: Implement ISerializable and Snapshot to PNG

您必须在这里执行的所有类都包含您的 BitmapImage 实施 ISerializable 接口,然后在 GetObjectData ,返回表示以下项编码的字节数组图片,例如PNG。然后在反序列化构造器中将PNG解码为新的 BitmapImage

What you must do here is have all classes that contain your BitmapImage implement the ISerializable interface, then, in GetObjectData, return a byte array representing an encoding of the image, for instance PNG. Then in the deserialization constructor decode the PNG to a new BitmapImage.

请注意,这会对图像进行快照,因此可能会丢失一些WPF数据。

由于您可能有多个包含 BitmapImage 的类,因此,最简单的方法是引入一个带有隐式转换为 BitmapImage ,例如:

Since you may have multiple classes that contain a BitmapImage, the easiest way to do this is to introduce some wrapper struct with an implicit conversion from and to BitmapImage, like so:

[Serializable]
public struct SerializableBitmapImageWrapper : ISerializable
{
    readonly BitmapImage bitmapImage;

    public static implicit operator BitmapImage(SerializableBitmapImageWrapper wrapper)
    {
        return wrapper.BitmapImage;
    }

    public static implicit operator SerializableBitmapImageWrapper(BitmapImage bitmapImage)
    {
        return new SerializableBitmapImageWrapper(bitmapImage);
    }

    public BitmapImage BitmapImage { get { return bitmapImage; } }

    public SerializableBitmapImageWrapper(BitmapImage bitmapImage)
    {
        this.bitmapImage = bitmapImage;
    }

    public SerializableBitmapImageWrapper(SerializationInfo info, StreamingContext context)
    {
        byte[] imageBytes = (byte[])info.GetValue("image", typeof(byte[]));
        if (imageBytes == null)
            bitmapImage = null;
        else
        {
            using (var ms = new MemoryStream(imageBytes))
            {
                var bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.StreamSource = ms;
                bitmap.EndInit();
                bitmapImage = bitmap;
            }
        }
    }

    #region ISerializable Members

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        byte [] imageBytes;
        if (bitmapImage == null)
            imageBytes = null;
        else
            using (var ms = new MemoryStream())
            {
                BitmapImage.SaveToPng(ms);
                imageBytes = ms.ToArray();
            }
        info.AddValue("image", imageBytes);
    }

    #endregion
}

public static class BitmapHelper
{
    public static void SaveToPng(this BitmapSource bitmap, Stream stream)
    {
        var encoder = new PngBitmapEncoder();
        SaveUsingEncoder(bitmap, stream, encoder);
    }

    public static void SaveUsingEncoder(this BitmapSource bitmap, Stream stream, BitmapEncoder encoder)
    {
        BitmapFrame frame = BitmapFrame.Create(bitmap);
        encoder.Frames.Add(frame);
        encoder.Save(stream);
    }

    public static BitmapImage FromUri(string path)
    {
        var bitmap = new BitmapImage();
        bitmap.BeginInit();
        bitmap.UriSource = new Uri(path);
        bitmap.EndInit();
        return bitmap;
    }
}

然后按以下方式使用它:

Then use it as follows:

[Serializable]
public class MyClass
{
    SerializableBitmapImageWrapper testImage;

    public string TestString { get; set; }
    public BitmapImage TestImage { get { return testImage; } set { testImage = value; } }
}

public static class GenericCopier
{
    public static T DeepCopy<T>(T objectToCopy)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, objectToCopy);
            memoryStream.Seek(0, SeekOrigin.Begin);
            return (T)binaryFormatter.Deserialize(memoryStream);
        }
    }
}

选项2:使用序列化代理直接克隆 BitmapImage

Option 2: Use Serialization Surrogates to Clone the BitmapImage Directly

事实证明, BitmapImage 具有 Clone() 方法,因此可以合理地问:是否有可能重写二进制序列化以替换原始序列化一个克隆,而没有实际序列化吗?这样做可以避免快照到PNG的潜在数据丢失,因此看起来更可取。

It turns out that BitmapImage has a Clone() method, so it is reasonable to ask: is it somehow possible to override binary serialization to replace the original with a clone, without actually serializing it? Doing so would avoid the potential data loss of snapshotting to PNG and would thus appear preferable.

事实上,可以使用序列化替代,以用 IObjectReference 代理,其中包含代理创建的克隆副本的ID。

In fact, this is possible using serialization surrogates to replace the bitmap images with an IObjectReference proxy containing an ID of a cloned copy created by the surrogate.

public static class GenericCopier
{
    public static T DeepCopy<T>(T objectToCopy)
    {
        var selector = new SurrogateSelector();
        var imageSurrogate = new BitmapImageCloneSurrogate();
        imageSurrogate.Register(selector);

        BinaryFormatter binaryFormatter = new BinaryFormatter(selector, new StreamingContext(StreamingContextStates.Clone));

        using (MemoryStream memoryStream = new MemoryStream())
        {
            binaryFormatter.Serialize(memoryStream, objectToCopy);
            memoryStream.Seek(0, SeekOrigin.Begin);
            return (T)binaryFormatter.Deserialize(memoryStream);
        }
    }
}

class CloneWrapper<T> : IObjectReference
{
    public T Clone { get; set; }

    #region IObjectReference Members

    object IObjectReference.GetRealObject(StreamingContext context)
    {
        return Clone;
    }

    #endregion
}

public abstract class CloneSurrogate<T> : ISerializationSurrogate where T : class
{
    readonly Dictionary<T, long> OriginalToId = new Dictionary<T, long>();
    readonly Dictionary<long, T> IdToClone = new Dictionary<long, T>();

    public void Register(SurrogateSelector selector)
    {
        foreach (var type in Types)
            selector.AddSurrogate(type, new StreamingContext(StreamingContextStates.Clone), this);
    }

    IEnumerable<Type> Types
    {
        get
        {
            yield return typeof(T);
            yield return typeof(CloneWrapper<T>);
        }
    }

    protected abstract T Clone(T original);

    #region ISerializationSurrogate Members

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        var original = (T)obj;
        long cloneId;
        if (original == null)
        {
            cloneId = -1;
        }
        else
        {
            if (!OriginalToId.TryGetValue(original, out cloneId))
            {
                Debug.Assert(OriginalToId.Count == IdToClone.Count);
                cloneId = OriginalToId.Count;
                OriginalToId[original] = cloneId;
                IdToClone[cloneId] = Clone(original);
            }
        }
        info.AddValue("cloneId", cloneId);
        info.SetType(typeof(CloneWrapper<T>));
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        var wrapper = (CloneWrapper<T>)obj;
        var cloneId = info.GetInt64("cloneId");
        if (cloneId != -1)
            wrapper.Clone = IdToClone[cloneId];
        return wrapper;
    }

    #endregion
}

public sealed class BitmapImageCloneSurrogate : CloneSurrogate<BitmapImage>
{
    protected override BitmapImage Clone(BitmapImage original)
    {
        return original == null ? null : original.Clone();
    }
}

在此实现中,您的主类保持不变:

In this implementation, your main classes remain unchanged:

[Serializable]
public class MyClass
{
    BitmapImage testImage;

    public string TestString { get; set; }
    public BitmapImage TestImage { get { return testImage; } set { testImage = value; } }
}

尴尬,而 BitmapImage 具有 Clone 方法,它实际上并未实现 ICloneable 接口。如果有的话,上面的代码看起来更简洁,因为我们可以简单地克隆每个可克隆对象,而不用为 BitmapImage 调用特定方法。

Awkwardly, while BitmapImage has a Clone method, it does not actually implement the ICloneable interface. If it had, the code above could look cleaner, because we could simply clone every cloneable object rather than calling a specific method for BitmapImage.

这篇关于如何序列化包含BitmapImage的类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆