WPF RichTextBox插入图像和撤消操作

时间:2015-09-18 09:09:21

标签: c# wpf xaml richtextbox

我有代码将图片从剪贴板导入RichtTextBox。

Image i = new Image();
i.Source =  Clipboard.GetImage();
paragraph.Inlines.Add(i);

当我尝试删除图像并按下撤销()时,我有例外。

  

在类型上找不到匹配的构造函数   ' System.Windows.Interop.InteropBitmap&#39 ;.您可以使用参数或   FactoryMethod指令构造此类型。'行号' 1'和   行位置' 226'。

这是因为RichTextBox生成的XAML如下所示:

<Image.Source><swi:InteropBitmap /></Image.Source>

我尝试将BitmapSource的类型更改为BitmapImage。但在这种情况下,我有XAML:

<Image.Source><BitmapImage BaseUri="{x:Null}" /></Image.Source></Image>

删除后,撤消我有异常:

  

抛出异常:&#39; System.Windows.Markup.XamlParseException&#39;在   PresentationFramework.dll

     

其他信息:&#39; System.Windows.Media.Imaging.BitmapImage&#39;的初始化抛出异常。&#39;线   数字&#39; 1&#39;和行位置&#39; 243&#39;。

我甚至尝试了来自http://wpftutorial.net/InlineImagesXaml.html

的InlineImage
<InlineImage Width="100" Height="100" Stretch="Fill">
   <![CDATA[iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAB3RJTUUH2AQP
        SFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAAnOSURBVHjaxVcLcBvVFT1vV
        ki3Hju3GCQnGjkObONQkJkxCSIHQQGnIdEr5TFs+LaGl7RRCSUvDp8nglH4mDGQ6EwZIm=]]>
    </InlineImage>

即使在这种情况下,我在撤消/重做操作中也有例外。有没有可能没有编写自己的撤销/重做操作来处理这种情况。

2 个答案:

答案 0 :(得分:1)

我很困惑天气编辑旧答案或添加新答案。所以我要换一个新的。

使用下面的方法我现在可以复制现有的图像文件并将其粘贴到RTB中,我现在也可以从MSPaint,Photoshop复制一些未保存的图像数据并粘贴它。按下“保存”按钮后,将保存rtf文件并按预期在MSWord中正常打开。

Ctrl + Z不起作用,因为图像数据在流中。我正在做。将图像复制为文件时,Ctrl + Z不是问题。

最受欢迎的是怀疑。以下代码完整可以按原样使用。

用于获取存储在剪贴板中的图像的ImageCode.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Runtime.InteropServices;

namespace WpfRichTextBox._32648134
{
    public class ImageCode
    {
        public static ImageSource ImageFromClipboardDibAsSource()
        {
            MemoryStream ms = Clipboard.GetData("DeviceIndependentBitmap") as MemoryStream;
            if (ms != null)
            {
                byte[] dibBuffer = new byte[ms.Length];
                ms.Read(dibBuffer, 0, dibBuffer.Length);

                BITMAPINFOHEADER infoHeader =
                    BinaryStructConverter.FromByteArray<BITMAPINFOHEADER>(dibBuffer);

                int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
                int infoHeaderSize = infoHeader.biSize;
                int fileSize = fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage;

                BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER();
                fileHeader.bfType = BITMAPFILEHEADER.BM;
                fileHeader.bfSize = fileSize;
                fileHeader.bfReserved1 = 0;
                fileHeader.bfReserved2 = 0;
                fileHeader.bfOffBits = fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4;

                byte[] fileHeaderBytes =
                    BinaryStructConverter.ToByteArray<BITMAPFILEHEADER>(fileHeader);

                MemoryStream msBitmap = new MemoryStream();
                msBitmap.Write(fileHeaderBytes, 0, fileHeaderSize);
                msBitmap.Write(dibBuffer, 0, dibBuffer.Length);
                msBitmap.Seek(0, SeekOrigin.Begin);

                BitmapImage img = new BitmapImage();
                img.BeginInit();
                img.CacheOption = BitmapCacheOption.OnDemand;
                img.CreateOptions = BitmapCreateOptions.DelayCreation;
                img.StreamSource = msBitmap;
                img.EndInit();

                return img;
            }
            return null;
        }

        public static MemoryStream ImageFromClipboardDibAsStream()
        {
            MemoryStream ms = Clipboard.GetData("DeviceIndependentBitmap") as MemoryStream;
            if (ms != null)
            {
                byte[] dibBuffer = new byte[ms.Length];
                ms.Read(dibBuffer, 0, dibBuffer.Length);

                BITMAPINFOHEADER infoHeader =
                    BinaryStructConverter.FromByteArray<BITMAPINFOHEADER>(dibBuffer);

                int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
                int infoHeaderSize = infoHeader.biSize;
                int fileSize = fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage;

                BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER();
                fileHeader.bfType = BITMAPFILEHEADER.BM;
                fileHeader.bfSize = fileSize;
                fileHeader.bfReserved1 = 0;
                fileHeader.bfReserved2 = 0;
                fileHeader.bfOffBits = fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4;

                byte[] fileHeaderBytes =
                    BinaryStructConverter.ToByteArray<BITMAPFILEHEADER>(fileHeader);

                MemoryStream msBitmap = new MemoryStream();
                msBitmap.Write(fileHeaderBytes, 0, fileHeaderSize);
                msBitmap.Write(dibBuffer, 0, dibBuffer.Length);
                msBitmap.Seek(0, SeekOrigin.Begin);

                return msBitmap;
            }
            return null;
        }
    }

    public static class BinaryStructConverter
    {
        public static T FromByteArray<T>(byte[] bytes) where T : struct
        {
            IntPtr ptr = IntPtr.Zero;
            try
            {
                int size = Marshal.SizeOf(typeof(T));
                ptr = Marshal.AllocHGlobal(size);
                Marshal.Copy(bytes, 0, ptr, size);
                object obj = Marshal.PtrToStructure(ptr, typeof(T));
                return (T)obj;
            }
            finally
            {
                if (ptr != IntPtr.Zero)
                    Marshal.FreeHGlobal(ptr);
            }
        }

        public static byte[] ToByteArray<T>(T obj) where T : struct
        {
            IntPtr ptr = IntPtr.Zero;
            try
            {
                int size = Marshal.SizeOf(typeof(T));
                ptr = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(obj, ptr, true);
                byte[] bytes = new byte[size];
                Marshal.Copy(ptr, bytes, 0, size);
                return bytes;
            }
            finally
            {
                if (ptr != IntPtr.Zero)
                    Marshal.FreeHGlobal(ptr);
            }
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 2)]
     struct BITMAPFILEHEADER
    {
        public static readonly short BM = 0x4d42; // BM

        public short bfType;
        public int bfSize;
        public short bfReserved1;
        public short bfReserved2;
        public int bfOffBits;
    }

    [StructLayout(LayoutKind.Sequential)]
     struct BITMAPINFOHEADER
    {
        public int biSize;
        public int biWidth;
        public int biHeight;
        public short biPlanes;
        public short biBitCount;
        public int biCompression;
        public int biSizeImage;
        public int biXPelsPerMeter;
        public int biYPelsPerMeter;
        public int biClrUsed;
        public int biClrImportant;
    }

}

MainWindow.xaml

<Window x:Class="WpfRichTextBox._32648134.Win32648134"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Win32648134" Height="600" Width="700">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="41*"/>
            <RowDefinition Height="5*"/>
            <RowDefinition Height="21*"/>
        </Grid.RowDefinitions>
        <RichTextBox x:Name="RtbCompose" Width="500" Height="300" ScrollViewer.VerticalScrollBarVisibility="Visible">
            <FlowDocument x:Name="FdDocument">
                <Paragraph x:Name="Para1"></Paragraph>
            </FlowDocument>
        </RichTextBox>        
        <Button x:Name="BtnCopyImgFile"  Content="Paste image" HorizontalAlignment="Left" Margin="96,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75" Click="BtnCopyImgFile_Click"/>
        <Button x:Name="BtnSave" Content="Save" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Top" Width="75" Margin="521,10,0,0" Click="BtnSave_Click"/>
        <Button x:Name="BtnCopyImgData" Content="Paste image data" HorizontalAlignment="Left" Margin="190,11,0,0" Grid.Row="1" VerticalAlignment="Top" Click="BtnCopyImgData_Click"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Diagnostics;
using System.IO;

namespace WpfRichTextBox._32648134
{
    /// <summary>
    /// Interaction logic for Win32648134.xaml
    /// </summary>
    public partial class Win32648134 : Window
    {
        public Win32648134()
        {
            InitializeComponent();
        }

        private void BtnCopyImgFile_Click(object sender, RoutedEventArgs e)
        {              
            Image i = new Image();

            if (Clipboard.ContainsFileDropList())
            {
                StringCollection fileNames = Clipboard.GetFileDropList();
                BitmapImage img = new BitmapImage(new Uri(fileNames[0], UriKind.Absolute));
                i.Source = img;
                Para1.Inlines.Add(i);
            }     

            Para1.Inlines.Add(new Run("first rtb app"));
        }

        private void BtnSave_Click(object sender, RoutedEventArgs e)
        {
            TextRange allText = new TextRange(RtbCompose.Document.ContentStart, RtbCompose.Document.ContentEnd);

            FileStream stream = new FileStream(@"I:\RTB.rtf", FileMode.Create);

            allText.Save(stream, DataFormats.Rtf);

            if (stream != null)
                stream.Close();
        }

        private void BtnCopyImgData_Click(object sender, RoutedEventArgs e)
        {
            bool hasImgData = Clipboard.ContainsImage();
            Image i = new Image();
            if (hasImgData)
            {
                BitmapSource imgData = (BitmapSource)ImageCode.ImageFromClipboardDibAsSource();
                i.Source = imgData;

                Para1.Inlines.Add(i);
            }

            Para1.Inlines.Add(new Run("rtb app, image comes from image data instead of file"));
        }
    }
}

答案 1 :(得分:0)

我找到了解决方案。我不得不从RichTextBox源代码中复制类WpfLoad。此代码将Package保存为Stream,其中Image为Content,Uri为文档的源。

因为类WpfPayload是内部的,所以我无权访问此类。我需要创建自己的。

以下是WpfPayLoad类的来源。

NoClassDefFoundError

接下来,如果我们在RichTextBox下面:

Foo bar = new Foo ();
System.out.println(Foo.class.getName());
System.out.println(bar.getClass().getName());

在Button_Click上我们可以从ClipBoard保存我们的图像。首先我得到了包的流,然后我们可以将它转换为XamlReader.Load()。

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
using System.Windows.Media.Imaging;

namespace YATE
{
    internal class WpfPayload
    {
        private const string XamlPayloadDirectory = "/Xaml"; // 
        private const string XamlEntryName = "/Document.xaml"; // 
        private const string XamlContentType = "application/vnd.ms-wpf.xaml+xml";
        private const string XamlImageName = "/Image"; // 
        private const string XamlRelationshipFromPackageToEntryPart = "http://schemas.microsoft.com/wpf/2005/10/xaml/entry";
        private const string XamlRelationshipFromXamlPartToComponentPart = "http://schemas.microsoft.com/wpf/2005/10/xaml/component";

        internal const string ImageBmpContentType = "image/bmp";
        private const string ImageGifContentType = "image/gif";
        private const string ImageJpegContentType = "image/jpeg";
        private const string ImageTiffContentType = "image/tiff";
        private const string ImagePngContentType = "image/png";

        private const string ImageBmpFileExtension = ".bmp";
        private const string ImageGifFileExtension = ".gif";
        private const string ImageJpegFileExtension = ".jpeg";
        private const string ImageJpgFileExtension = ".jpg";
        private const string ImageTiffFileExtension = ".tiff";
        private const string ImagePngFileExtension = ".png";

        Package _package = null;

        private static BitmapEncoder GetBitmapEncoder(string imageContentType)
        {
            BitmapEncoder bitmapEncoder;

            switch (imageContentType)
            {
                case ImageBmpContentType:
                    bitmapEncoder = new BmpBitmapEncoder();
                    break;
                case ImageGifContentType:
                    bitmapEncoder = new GifBitmapEncoder();
                    break;
                case ImageJpegContentType:
                    bitmapEncoder = new JpegBitmapEncoder();
                    // 

                    break;
                case ImageTiffContentType:
                    bitmapEncoder = new TiffBitmapEncoder();
                    break;
                case ImagePngContentType:
                    bitmapEncoder = new PngBitmapEncoder();
                    break;
                default:
                    bitmapEncoder = null;
                    break;
            }
            return bitmapEncoder;
        }

        // Returns a file extension corresponding to a given imageContentType
        private static string GetImageFileExtension(string imageContentType)
        {
            string imageFileExtension;
            switch (imageContentType)
            {
                case ImageBmpContentType:
                    imageFileExtension = ImageBmpFileExtension;
                    break;
                case ImageGifContentType:
                    imageFileExtension = ImageGifFileExtension;
                    break;
                case ImageJpegContentType:
                    imageFileExtension = ImageJpegFileExtension;
                    break;
                case ImageTiffContentType:
                    imageFileExtension = ImageTiffFileExtension;
                    break;
                case ImagePngContentType:
                    imageFileExtension = ImagePngFileExtension;
                    break;
                default:
                    imageFileExtension = null;
                    break;
            }
            return imageFileExtension;
        }

        WpfPayload(Package p = null)
        {
            this._package = p;
        }

        private Package CreatePackage(Stream stream)
        {
            _package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite);
            return _package;
        }


        // Generates a image part Uri for the given image index
        private static string GetImageName(int imageIndex, string imageContentType)
        {
            string imageFileExtension = GetImageFileExtension(imageContentType);
            return XamlImageName + (imageIndex + 1) + imageFileExtension;
        }

        // Generates a relative URL for using from within xaml Image tag.
        private static string GetImageReference(string imageName)
        {
            return "." + imageName; // imageName is supposed to be created by GetImageName method
        }

        private PackagePart CreateWpfEntryPart()
        {
            // Define an entry part uri
            Uri entryPartUri = new Uri(XamlPayloadDirectory + XamlEntryName, UriKind.Relative);

            // Create the main xaml part
            PackagePart part = _package.CreatePart(entryPartUri, XamlContentType, CompressionOption.Normal);
            // Compression is turned off in this mode.
            //NotCompressed = -1,
            // Compression is optimized for a resonable compromise between size and performance. 
            //Normal = 0,
            // Compression is optimized for size. 
            //Maximum = 1,
            // Compression is optimized for performance. 
            //Fast = 2 ,
            // Compression is optimized for super performance. 
            //SuperFast = 3,

            // Create the relationship referring to the entry part
            PackageRelationship entryRelationship = _package.CreateRelationship(entryPartUri, TargetMode.Internal, XamlRelationshipFromPackageToEntryPart);

            return part;
        }

        private void CreateImagePart(PackagePart sourcePart, BitmapSource imageSource, string imageContentType, int imageIndex)
        {
            // Generate a new unique image part name
            string imagePartUriString = GetImageName(imageIndex, imageContentType);

            // Define an image part uri
            Uri imagePartUri = new Uri(XamlPayloadDirectory + imagePartUriString, UriKind.Relative);

            // Create a part for the image
            PackagePart imagePart = _package.CreatePart(imagePartUri, imageContentType, CompressionOption.NotCompressed);

            // Create the relationship referring from the enrty part to the image part
            PackageRelationship componentRelationship = sourcePart.CreateRelationship(imagePartUri, TargetMode.Internal, XamlRelationshipFromXamlPartToComponentPart);

            // Encode the image data
            BitmapEncoder bitmapEncoder = GetBitmapEncoder(imageContentType);
            bitmapEncoder.Frames.Add(BitmapFrame.Create(imageSource));

            // Save encoded image data into the image part in the package
            Stream imageStream = imagePart.GetStream();
            using (imageStream)
            {
                bitmapEncoder.Save(imageStream);
            }
        }


        internal PackagePart GetWpfEntryPart()
        {
            PackagePart wpfEntryPart = null;

            // Find a relationship to entry part
            PackageRelationshipCollection entryPartRelationships = _package.GetRelationshipsByType(XamlRelationshipFromPackageToEntryPart);
            PackageRelationship entryPartRelationship = null;
            foreach (PackageRelationship packageRelationship in entryPartRelationships)
            {
                entryPartRelationship = packageRelationship;
                break;
            }

            // Get a part referred by this relationship
            if (entryPartRelationship != null)
            {
                // Get entry part uri
                Uri entryPartUri = entryPartRelationship.TargetUri;

                // Get the enrty part
                wpfEntryPart = _package.GetPart(entryPartUri);
            }

            return wpfEntryPart;
        }



        [System.Security.SecurityCritical]
        internal static Stream SaveImage(BitmapSource bitmapSource, string imageContentType)
        {
            MemoryStream stream = new MemoryStream();
            // Create the wpf package in the stream
            WpfPayload wpfPayload = new WpfPayload();
            using (wpfPayload.CreatePackage(stream))
            {
                PackagePart xamlEntryPart = wpfPayload.CreateWpfEntryPart();

                Stream xamlPartStream = xamlEntryPart.GetStream();
                using (xamlPartStream)
                {
                    int imageIndex = 0;
                    string imageReference = GetImageReference(GetImageName(imageIndex, imageContentType));

                    StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream);
                    using (xamlPartWriter)
                    {
                        string xamlText =
                            "<Span xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">" +
                            "<InlineUIContainer><Image " +
                            "Width=\"" +
                            bitmapSource.Width + "\" " +
                            "Height=\"" +
                            bitmapSource.Height + "\" " +
                            "><Image.Source><BitmapImage CacheOption=\"OnLoad\" UriSource=\"" +
                            imageReference +
                            "\"/></Image.Source></Image></InlineUIContainer></Span>";
                        xamlPartWriter.Write(xamlText);
                    }
                    wpfPayload.CreateImagePart(xamlEntryPart, bitmapSource, imageContentType, imageIndex);
                }
            }
            return stream;
        }

        static int _wpfPayloadCount; // used to disambiguate between all acts of loading from different WPF payloads.


        internal static object LoadElement(Stream stream)
        {
            Package package = Package.Open(stream, FileMode.Open, FileAccess.Read);
            WpfPayload wpfPayload = new WpfPayload(package);

            PackagePart xamlEntryPart = wpfPayload.GetWpfEntryPart();


            int newWpfPayoutCount = _wpfPayloadCount++;
            Uri payloadUri = new Uri("payload://wpf" + newWpfPayoutCount, UriKind.Absolute);
            Uri entryPartUri = PackUriHelper.Create(payloadUri, xamlEntryPart.Uri); // gives an absolute uri of the entry part
            Uri packageUri = PackUriHelper.GetPackageUri(entryPartUri); // extracts package uri from combined package+part uri
            PackageStore.AddPackage(packageUri, wpfPayload.Package); // Register the package
            ParserContext parserContext = new ParserContext();
            parserContext.BaseUri = entryPartUri;

            object xamlObject = XamlReader.Load(xamlEntryPart.GetStream(), parserContext);
            // Remove the temporary uri from the PackageStore
            PackageStore.RemovePackage(packageUri);

            return xamlObject;
        }

        public Package Package
        {
            get { return _package; }
        }

    };
}

我们可以使用XAMLPackage保存结果。

<RichTextBox x:Name="RtbCompose" Width="500" Height="300">
            <FlowDocument x:Name="FdDocument">
                <Paragraph x:Name="Para1"></Paragraph>
            </FlowDocument>
</RichTextBox>        
<Button  Content="Paste image" HorizontalAlignment="Left" Margin="96,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75" Click="Button_Click"/>

使用此解决方案,Undo()和Redo()可以正常工作:)