c# - How to serialize a Class contains BitmapImage? -
i have deepcopy method, serializes object passed in parameter , returns deserialized object make deep copy.
my method is:
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); } } }
it works well, if object passed parameter doesn't contain bitmapimage field , properties.
public class myclass { public string teststring {get; set;} public bitmapimage testimage { get; set;} }
if make deepcopy of myclass,
myclass orginal = new myclass(){ teststring = "test"}; myclass copy = genericcopier<myclass>.deepcopy(orginal);
it throws exception
type 'system.windows.media.imaging.bitmapimage' in assembly not marked serializable
i found method serialize bitmapimage here
but, how can mix both type of serialization (binaryformatter & pngbitmapencoder) serialize myclass?
you have 2 options here:
option 1: implement iserializable
, snapshot png
what must here have classes contain bitmapimage
implement iserializable
interface, then, in getobjectdata
, return byte array representing encoding of image, instance png. in deserialization constructor decode png new bitmapimage
.
note snapshots image , may lose wpf data.
since may have multiple classes contain bitmapimage
, easiest way introduce wrapper struct implicit conversion , bitmapimage
, 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 { { 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 follows:
[serializable] public class myclass { serializablebitmapimagewrapper testimage; public string teststring { get; set; } public bitmapimage testimage { { 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); } } }
option 2: use serialization surrogates clone bitmapimage
directly
it turns out bitmapimage
has clone()
method, reasonable ask: somehow possible override binary serialization replace original clone, without serializing it? doing avoid potential data loss of snapshotting png , appear preferable.
in fact, possible using serialization surrogates replace bitmap images iobjectreference
proxy containing id of cloned copy created 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 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 { { 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 implementation, main classes remain unchanged:
[serializable] public class myclass { bitmapimage testimage; public string teststring { get; set; } public bitmapimage testimage { { return testimage; } set { testimage = value; } } }
awkwardly, while bitmapimage
has clone
method, not implement icloneable
interface. if had, code above cleaner, because clone every cloneable object rather calling specific method bitmapimage
.
Comments
Post a Comment