2015-04-01 79 views
1

我有一個DeepCopy方法,它將參數中傳遞的對象序列化並返回反序列化的對象以進行深度複製。如何序列化一個類包含BitmapImage?

我的方法是:

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場和屬性。

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

如果我做的MyClass deepcopy的,

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

它拋出異常大會

類型 'System.Windows.Media.Imaging.BitmapImage' 未標記爲可序列

我發現了一種方法來序列化BitmapImage here

但是,我怎樣才能將兩種類型的序列化(BinaryFormatter & PngBitmapEncoder)混合來序列化MyClass?

+0

如果可能的話,我會建議將圖像存儲爲byte []字段,然後在屬性中重新創建它。請注意,BinaryFormatter僅序列化字段。 – sanke 2015-04-01 17:49:53

+0

如果我使用byte []類型而不是BitmapImage,如何在xaml中的Image控件的Source屬性中綁定byte []? – 2015-04-01 17:59:53

+0

使用BitmapImage屬性,您可以在屬性getter中從字節[]字段重建圖像,其中示例說明如何從字節數組中重建字符。對於你的情況,它也不是最理想的方式,你應該重複使用你的圖像,而不是複製它們,有人可以更好地解釋 – sanke 2015-04-01 18:06:48

回答

0

你這裏有兩種選擇:

選項1:實施ISerializable和快照以PNG

那你必須在這裏做的是有一個包含所有類的BitmapImage實現ISerializable接口,然後,在GetObjectData,返回表示圖像編碼的字節數組,例如PNG。然後在deserialization constructor將PNG解碼爲新的BitmapImage

請注意,此快照圖像,因此可能會丟失一些WPF數據。

因爲你可能有包含BitmapImage多個類,要做到這一點最簡單的方法是用隱式轉換介紹一些包裝結構從和BitmapImage,像這樣:

[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; 
    } 
} 

然後用它如下:

[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直接

事實證明,BitmapImage有一個Clone()方法,所以有必要問一下:是否有可能重寫二進制序列化以用克隆替換原始文件,而不實際序列化它?這樣做可以避免快照到PNG的潛在數據丟失,因此看起來更可取。

實際上,這可以使用serialization surrogates來替換包含由代理創建的克隆拷貝的ID的IObjectReference代理的位圖圖像。

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(); 
    } 
} 

在此實現,您的主要類別保持不變:

[Serializable] 
public class MyClass 
{ 
    BitmapImage testImage; 

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

笨拙,而BitmapImageClone方法,它實際上並沒有實現ICloneable接口。如果有的話,上面的代碼可能看起來更乾淨,因爲我們可以簡單地克隆每個可複製對象,而不是調用BitmapImage的特定方法。

相關問題