当我尝试从 WPF 中的 RichTextBox 手动删除图像时出现 InvalidOperationException
InvalidOperationException when I try to delete an image manually from RichTextBox in WPF
我正在尝试创建一个应用程序,我可以在其中将文本和图像输入到 RichTextBox 中,将其序列化,然后将其反序列化并将其加载回 RichTextBox,以便我以后可以更改它。当我从序列化的 xml 文件加载图像时,一切都正确显示,但是当我尝试通过按退格键手动从 RichTextBox 中删除图像时,出现以下异常:无法序列化非 public 输入 'System.Windows.Media.Imaging.BitmapFrameDecode'.
以下是我从 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;
}
这是我序列化和反序列化所有内容的地方 to/from 一个 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;
}
}
如有任何帮助,我们将不胜感激。
当用户从 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
is abstract, and the type actually returned by BitmapDecoder.Frames.First()
, BitmapFrameDecode
, is internal, and thus 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.BaseUri
was null when the BitmapImage
was created from a memory stream rather than from a 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
编码字节数组。
我正在尝试创建一个应用程序,我可以在其中将文本和图像输入到 RichTextBox 中,将其序列化,然后将其反序列化并将其加载回 RichTextBox,以便我以后可以更改它。当我从序列化的 xml 文件加载图像时,一切都正确显示,但是当我尝试通过按退格键手动从 RichTextBox 中删除图像时,出现以下异常:无法序列化非 public 输入 'System.Windows.Media.Imaging.BitmapFrameDecode'.
以下是我从 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;
}
这是我序列化和反序列化所有内容的地方 to/from 一个 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;
}
}
如有任何帮助,我们将不胜感激。
当用户从 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
is abstract, and the type actually returned by BitmapDecoder.Frames.First()
, BitmapFrameDecode
, is internal, and thus 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.BaseUri
was null when the BitmapImage
was created from a memory stream rather than from a 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
编码字节数组。