2015-05-14 51 views
3

我想創建一個應用程序,我可以在RichTextBox中輸入文本和圖像,將其序列化,然後反序列化並將其加載回RichTextBox,以便我可以稍後改變它。當我從序列化的xml文件加載圖像時,所有內容都能正確顯示,但是當我嘗試通過按下退格鍵手動從RichTextBox中刪除圖像時,出現以下異常:無法序列化非公開類型的「System.Windows.Media .Imaging.BitmapFrameDecode」。InvalidOperationException當我嘗試從WPF中的RichTextBox手動刪除圖像

下面是我如何從RichTextBox中提取和存儲數據。它檢查所有塊,如果發現一個圖像,然後它只是在列表中保存的文本佔位符串存在,這樣,當它返回它就會知道把圖像回到該位置:

public void GetFindingsData(FlowDocument flowDoc, List<string> text, List<byte[]> bytes) 
    { 
     foreach (Block block in flowDoc.Blocks) 
     { 
      if (block.GetType() == typeof(Paragraph)) 
      { 
       foreach (Run run in ((Paragraph)block).Inlines) 
       { 
        text.Add(run.Text); 
       } 
      } 

      else if (block.GetType() == typeof(BlockUIContainer) && ((BlockUIContainer)block).Child.GetType() == typeof(Image)) 
      { 
       Image img = (Image)((BlockUIContainer)block).Child; 
       bytes.Add(Storage.ImageToByteArray(img)); 
       text.Add("imageplaceholder_" + (bytes.Count - 1).ToString()); 
      } 
     } 
    } 

這裏就是我如何把這些數據返回到一個FlowDocument的顯示在RichTextBox:

public FlowDocument createFlowDocument(List<string> runs, List<byte[]> bytes) 
    { 
     FlowDocument flowDoc = new FlowDocument(); 
     int counter = 0; 
     foreach (string run in runs) 
     { 
      if (run == "imageplaceholder_" + counter.ToString()) 
      { 
       flowDoc.Blocks.Add(new BlockUIContainer(Storage.ByteArrayToImage(bytes[counter]))); 
       counter++; 
      } 

      else 
      { 
       Paragraph par = new Paragraph(); 
       par.Inlines.Add(run); 
       flowDoc.Blocks.Add(par); 
      } 
     } 

     return flowDoc; 
    } 

如果你需要它,這裏就是我從RichTextBox的序列化數據。我的所有其他數據序列化爲XML,但是,這並不對圖像的工作,所以我首先它序列化到一個字節數組:

public static byte[] ImageToByteArray(Image image) 
    { 
     byte[] imageBuffer = null; 

     if (image != null) 
     { 
      using (var stream = new MemoryStream()) 
      { 
       var encoder = new PngBitmapEncoder(); 
       encoder.Frames.Add(BitmapFrame.Create(image.Source as BitmapSource)); 
       encoder.Save(stream); 
       imageBuffer = stream.ToArray(); 
      } 
     } 

     return imageBuffer; 
    } 

這是我序列化和從XML文件反序列化的一切/(雖然我不認爲這個問題是在這裏):

public static void SaveData(StoredData data) 
    { 
      XmlSerializer serializer = new XmlSerializer(typeof(StoredData)); 
      using (FileStream stream = new FileStream(readerString, FileMode.Create, FileAccess.Write, FileShare.None)) 
      { 
       serializer.Serialize(stream, data); 
      } 
    } 

public static StoredData LoadData() 
    { 
     try 
     { 
      StoredData storedData = new StoredData(); 
      using (FileStream stream = new FileStream(readerString, FileMode.Open)) 
      { 
       XmlSerializer deserializer = new XmlSerializer(typeof(StoredData)); 
       storedData = (StoredData)deserializer.Deserialize(stream); 
      } 
      return storedData; 

     } 

     catch 
     { 
      StoredData newData = new StoredData(); 

      XmlSerializer serializer = new XmlSerializer(typeof (StoredData)); 
      using (FileStream stream = new FileStream(readerString, FileMode.Create, FileAccess.Write, FileShare.None)) 
      { 
       serializer.Serialize(stream, newData); 
      } 

      return newData; 
     } 
    } 

這裏就是我得到的圖像回從字節數組:

public static Image ByteArrayToImage(Byte[] imageBytes) 
    { 
     using (MemoryStream stream = new MemoryStream(imageBytes)) 
     { 
      BitmapDecoder decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad); 

      BitmapFrame frame = decoder.Frames.First(); 

      frame.Freeze(); 
      Image newImage = new Image(); 
      newImage.Source = frame; 
      return newImage; 
     } 
    } 

任何幫助將不勝感激。

+0

您是否看到[link](http://stackoverflow.com/questions/1820098/c-sharp-wpf-convert-bitmapimage-pasted-in-richtextbox-to-binary)?這似乎與你的問題類似。 –

+0

我做過了,如果我錯了,請糾正我的錯誤,但似乎他們只是暫時做了同樣的問題,然後才決定採用不同的方法來解決問題。如果我沒有足夠強調我的問題,我沒有任何問題從RichTextBox保存或加載到RichTextBox。只有當我嘗試刪除重新載入的圖片時,我遇到了這個錯誤。即使刪除重新加載的文本也能正常工 – mrmry

+0

是的,我知道了。那麼,當你得到這個異常?在哪一行?你在RichTextBox的文字改變事件中做什麼? –

回答

0

當用戶從RichTextBox中刪除圖像時,它將已刪除的項目序列化爲XAML。這從堆棧跟蹤是顯而易見的:

at System.Windows.Markup.Primitives.MarkupWriter.VerifyTypeIsSerializable(Type type) 
    at System.Windows.Markup.Primitives.MarkupWriter.WriteItem(MarkupObject item, Scope scope) 
    at System.Windows.Markup.Primitives.MarkupWriter.WriteItem(MarkupObject item, Scope scope) 
    at System.Windows.Markup.Primitives.MarkupWriter.WriteItem(MarkupObject item) 
    at System.Windows.Markup.Primitives.MarkupWriter.SaveAsXml(XmlWriter writer, MarkupObject item) 
    at System.Windows.Markup.Primitives.MarkupWriter.SaveAsXml(XmlWriter writer, Object instance) 
    at System.Windows.Markup.XamlWriter.Save(Object obj, TextWriter writer) 
    at System.Windows.Markup.XamlWriter.Save(Object obj) 
    at System.Windows.Documents.TextTreeDeleteContentUndoUnit.CopyObjectNode(TextTreeObjectNode objectNode, ContentContainer& container) 
    at System.Windows.Documents.TextTreeDeleteContentUndoUnit.CopyContent(TextTreeNode node, TextTreeNode haltNode) 
    at System.Windows.Documents.TextTreeDeleteContentUndoUnit..ctor(TextContainer tree, TextPointer start, TextPointer end) 
    at System.Windows.Documents.TextTreeUndo.CreateDeleteContentUndoUnit(TextContainer tree, TextPointer start, TextPointer end) 
    at System.Windows.Documents.TextContainer.DeleteContentInternal(TextPointer startPosition, TextPointer endPosition) 

這會導致你的問題,因爲類型BitmapFrame抽象,類型實際上是由BitmapDecoder.Frames.First()BitmapFrameDecode返回,是內部,從而can't be serialized to XAML。我不明白你爲什麼需要使用BitmapDecoder。爲什麼不使用常規的BitmapImage?如果我用下面的代碼加載圖像,插入的圖像,現在可以刪除:

public static Image ByteArrayToImage(Byte[] imageBytes) 
    { 
     var stream = new MemoryStream(imageBytes); 
     { 
      var frame = new BitmapImage(); 
      frame.BeginInit(); 
      frame.CacheOption = BitmapCacheOption.OnLoad; 
      frame.StreamSource = stream; 
      frame.EndInit(); 
      frame.Freeze(); 
      Image newImage = new Image() { Source = frame }; 
      return newImage; 
     } 
    } 

好了,在做這件事,我發現了一個次要的問題:如果我試圖撤消刪除它後刪除圖像,它不會回來。相反,具有空圖像的垃圾BlockUIContainer將被恢復。我無法確定發生這種情況的確切原因,但它與BitmapImage.BaseUriBitmapImage是從存儲器流創建而不是從UriSource創建時爲空的事實有關。

我能解決這個問題,反過來,通過序列化到一個臨時XamlPackage

public static void AddBlockUIContainerImage(this FlowDocument doc, byte[] imageBytes) 
    { 
     var image2 = Storage.ByteArrayToImage(imageBytes); 
     using (var stream = new MemoryStream()) 
     { 
      var subDoc = new FlowDocument(); 
      subDoc.Blocks.Add(new BlockUIContainer(image2)); 
      new TextRange(subDoc.ContentStart, subDoc.ContentEnd).Save(stream, DataFormats.XamlPackage, true); 
      stream.Seek(0, SeekOrigin.Begin); 
      var target = new TextRange(doc.ContentEnd, doc.ContentEnd); 
      target.Load(stream, DataFormats.XamlPackage); 
     } 
    } 

已經做到了這一點,在BaseUri不再空;撤消,重做,刪除,撤銷刪除和重做刪除所有工作。

由於此解決方法意味着您有效地反序列化圖像兩次,因此您可能需要考慮將每個圖像存儲爲XamlPackage編碼的字節數組,而不是Png編碼的字節數組。

+0

工程就像一個魅力,我不能夠感謝你!我在尋找答案時遇到的一個問題是,我不知道當您通過按下退格鍵從WPF中的文本框中刪除某些內容時會發生什麼情況,並且我不確定要查找哪些內容。使用術語「刪除」搜索傾向於給出關於程序刪除的答案,而「退格」給出了關於捕獲和更改退格事件的很多答案(雖然我想知道默認情況下發生了什麼)。是否有一種資源涵蓋了文本框事件的幕後發生情況,如複製,刪除等? – mrmry

+0

@mrmry - 我不知道有什麼好的資源來理解RichTextBox和FlowDocument是如何工作的。即使[參考源代碼](http://referencesource.microsoft.com/#PresentationFramework/Framework/System/Windows/Documents/FlowDocument.cs)也無濟於事,因爲很多工作都是在子元素或輔助類中完成的。如果試圖查看「FlowDocument」的結構,我發現轉儲到XAML有幫助。 – dbc

相關問題