我尝试从WPF中的RichTextBox手动删除图像时出现InvalidOperationException

时间:2015-05-14 13:56:02

标签: c# xml wpf image richtextbox

我正在尝试创建一个应用程序,我可以在其中输入文本和图像到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;
        }
    }

非常感谢任何帮助。

1 个答案:

答案 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编码的字节数组。