我正在尝试创建一个应用程序,我可以在其中输入文本和图像到RichTextBox,序列化它,然后反序列化它并将其加载回RichTextBox,以便我以后可以更改它。当我从序列化的xml文件加载图像时,一切都正确显示,但是当我尝试通过按退格键手动从RichTextBox中删除图像时,我得到以下异常:无法序列化非公共类型'System.Windows.Media .Imaging.BitmapFrameDecode”。
以下是我从RichTextBox中提取和存储数据的方法。它检查所有块,如果找到图像,那么它只是在List文本中保存占位符字符串,这样当它返回时它就会知道将图像放回到该位置:
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 :(得分:0)
当用户从RichTextBox
删除图像时,将已删除的项目copies通过序列化为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
从内存流而不是BitmapImage.BaseUri
创建 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);
}
}
时,它与UriSource
为空的事实有关。
我能够通过序列化到临时XamlPackage
来解决这个问题:
BaseUri
完成此操作后,XamlPackage
不再为空; undo,redo,delete,undo delete和redo delete all working。
由于此解决方法意味着您有效地将图像反序列化两次,因此您可能需要考虑将每个图像存储为Png
编码的字节数组而不是java.util.Properties
编码的字节数组。