Comment sérialiser une classe contient BitmapImage?

J’ai une méthode DeepCopy, qui sérialise l’object passé en paramètre et renvoie l’object désérialisé pour effectuer une copie complète.

Ma méthode est:

public static class GenericCopier { 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); } } } 

Cela fonctionne bien si l’object passé au paramètre ne contient aucun champ et propriétés BitmapImage.

 public class MyClass { public ssortingng TestSsortingng {get; set;} public BitmapImage TestImage { get; set;} } 

Si je fais DeepCopy de MyClass,

 MyClass orginal = new MyClass(){ TestSsortingng = "Test"}; MyClass copy = GenericCopier.DeepCopy(orginal); 

il jette exception

Le type ‘System.Windows.Media.Imaging.BitmapImage’ dans Assembly n’est pas marqué comme sérialisable.

J’ai trouvé une méthode pour sérialiser BitmapImage ici

Mais, comment puis-je mélanger les deux types de sérialisation (BinaryFormatter & PngBitmapEncoder) pour sérialiser MyClass?

Vous avez deux options ici:

Option 1: implémenter ISerializable et Snapshot to PNG

Ce que vous devez faire ici est que toutes les classes contenant votre BitmapImage implémentent l’interface ISerializable , puis, dans GetObjectData , renvoient un tableau d’octets représentant un codage de l’image, par exemple PNG. Puis, dans le constructeur de désérialisation, BitmapImage PNG en une nouvelle BitmapImage .

Notez que cela capture l’image et risque donc de perdre certaines données WPF.

Comme vous pouvez avoir plusieurs classes contenant une image BitmapImage , le moyen le plus simple consiste à introduire une structure wrapper avec une conversion implicite de et vers BitmapImage , comme ceci:

 [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(ssortingng path) { var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.UriSource = new Uri(path); bitmap.EndInit(); return bitmap; } } 

Puis utilisez-le comme suit:

 [Serializable] public class MyClass { SerializableBitmapImageWrapper testImage; public ssortingng TestSsortingng { get; set; } public BitmapImage TestImage { get { return testImage; } set { testImage = value; } } } public static class GenericCopier { public static T DeepCopy(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: utiliser des substitutions de sérialisation pour BitmapImage directement l’image BitmapImage

BitmapImage possède une méthode Clone() . Il est donc raisonnable de se demander: est-il possible de remplacer la sérialisation binary pour remplacer l’original par un clone, sans le sérialiser? Cela éviterait la perte potentielle de données de la capture instantanée au format PNG et semblerait donc préférable.

En fait, cela est possible en utilisant des substituts de sérialisation pour remplacer les images bitmap par un proxy IObjectReference contenant l’ID d’une copie clonée créée par le substitut.

 public static class GenericCopier { public static T DeepCopy(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 : IObjectReference { public T Clone { get; set; } #region IObjectReference Members object IObjectReference.GetRealObject(StreamingContext context) { return Clone; } #endregion } public abstract class CloneSurrogate : ISerializationSurrogate where T : class { readonly Dictionary OriginalToId = new Dictionary(); readonly Dictionary IdToClone = new Dictionary(); public void Register(SurrogateSelector selector) { foreach (var type in Types) selector.AddSurrogate(type, new StreamingContext(StreamingContextStates.Clone), this); } IEnumerable Types { get { yield return typeof(T); yield return typeof(CloneWrapper); } } 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)); } public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { var wrapper = (CloneWrapper)obj; var cloneId = info.GetInt64("cloneId"); if (cloneId != -1) wrapper.Clone = IdToClone[cloneId]; return wrapper; } #endregion } public sealed class BitmapImageCloneSurrogate : CloneSurrogate { protected override BitmapImage Clone(BitmapImage original) { return original == null ? null : original.Clone(); } } 

Dans cette implémentation, vos classes principales restnt inchangées:

 [Serializable] public class MyClass { BitmapImage testImage; public ssortingng TestSsortingng { get; set; } public BitmapImage TestImage { get { return testImage; } set { testImage = value; } } } 

Maladroitement, bien que BitmapImage ait une méthode Clone , elle BitmapImage pas l’interface ICloneable . Si c’était le cas, le code ci-dessus pourrait paraître plus propre, car nous pourrions simplement cloner chaque object clonable au lieu d’appeler une méthode spécifique pour BitmapImage .