【问题标题】:Insert Image at Cursor Position in Rich Text box在富文本框中的光标位置插入图像
【发布时间】:2013-08-03 17:35:32
【问题描述】:

我知道有很多这样的问题,但我之所以问是因为我无法理解所有的答案。我有RichTextBox,我希望用户能够在当前光标位置插入图像。

我尝试使用Clipboard 设置图像,然后将其粘贴到富文本框中。这行得通,但有人告诉我这是不好的做法,因为它会在不通知用户的情况下更改剪贴板中的数据。

这是我尝试过的

    private bool CheckIfImage(string filename)
    {
        if (filename.EndsWith(".jpeg")) { return true; }
        else if (filename.EndsWith(".jpg")) { return true; }
        else if (filename.EndsWith(".png")) { return true; }
        else if (filename.EndsWith(".ico")) { return true; }
        else if (filename.EndsWith(".gif")) { return true; }
        else if (filename.EndsWith(".bmp")) { return true; }
        else if (filename.EndsWith(".emp")) { return true; }
        else if (filename.EndsWith(".wmf")) { return true; }
        else if (filename.EndsWith(".tiff")) { return true; }
        else { return false; }
    }

    private void openFileDialog2_FileOk(object sender, CancelEventArgs e)
    {
        if (CheckIfImage(openFileDialog2.FileName.ToLower()) == true)
        {
            Image img = Image.FromFile(openFileDialog2.FileName);
            string setData = (String)Clipboard.GetData(DataFormats.Rtf);

            Clipboard.SetImage(img);
            rtbType.Paste();

            Clipboard.SetData(DataFormats.Rtf, setData);
        }
        else
        {
            MessageBox.Show("Invalid Image File Selected");
        } 
    }

请问有没有更好的办法呢?

【问题讨论】:

标签: c# winforms image richtextbox


【解决方案1】:

RichTextBox 对 OLE(对象链接和嵌入)的支持是历史性的意外。 OLE 是一项死技术,多年来已被严重弃用。它的丧钟当然是 .NET 完全不支持它。从本机 RichEdit 控件中删除 OLE 支持本来是明智的,但它会破坏太多古老的应用程序。 .NET RichTextBox 类本身只是本机组件的一个小包装器,不会添加或减少该组件的功能。

因此,在 .NET 中使用 OLE api 没有任何简单的方法。通过剪贴板复制/粘贴仍然有效的事实只是一个意外,.NET 不参与该操作,因此无法阻止它。

所以是的,它仍然可以通过剪贴板工作,并且是使用该功能的唯一体面方式。当然还有更好的选择,比如 WebBrowser 或 Word interop 可以为您提供更大的灵活性。 PDF 包装器很受欢迎。 WPF 很好地支持复合文档。等等。

【讨论】:

  • 所以基本上,他应该创建自己的TextBox(或RichTextBox)包装器。
  • @Precious1tj 其实你想要的都可以解决但是比较复杂,这里有链接供你研究更多codeproject.com/Articles/4544/…
【解决方案2】:

您可以尝试使用 WPF RichTextBox 并将其托管在您的 WinForms 窗口中。您将能够使用 WPF 非常轻松地在光标位置插入图像。

【讨论】:

    【解决方案3】:

    我已经为您准备了一个功能齐全的示例,使用发布的解决方案 here 利用 RTF 功能。

    正如 Hans Passant 所写:解决方案相当棘手,有一些有效的替代方案可以实现。

    顺便说一句,这是你的代码(重写):

    private bool CheckIfImage(string filename)
    {
            var valids = new[] {".jpeg", ".jpg", ".png", ".ico", ".gif", ".bmp", ".emp", ".wmf", ".tiff"};
            return valids.Contains(System.IO.Path.GetExtension(filename));
    }
    
    private void openFileDialog2_FileOk(object sender, CancelEventArgs e)
    {
        if (CheckIfImage(openFileDialog2.FileName.ToLower()) == true)
            embedImage(Image.FromFile(openFileDialog2.FileName));
        else
            MessageBox.Show("Invalid Image File Selected");
    }
    

    这是embedImage 方法:

        private void embedImage(Image img)
        {
            var rtf = new StringBuilder();
    
            // Append the RTF header
            rtf.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033");
            // Create the font table using the RichTextBox's current font and append
            // it to the RTF string
            rtf.Append(GetFontTable(this.Font));
            // Create the image control string and append it to the RTF string
            rtf.Append(GetImagePrefix(img));
            // Create the Windows Metafile and append its bytes in HEX format
            rtf.Append(getRtfImage(img));
            // Close the RTF image control string
            rtf.Append(@"}");
            richTextBox1.SelectedRtf = rtf.ToString();
        }
    

    这里有所有必要的方法:

        private enum EmfToWmfBitsFlags
        {
            EmfToWmfBitsFlagsDefault = 0x00000000,
            EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
            EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
            EmfToWmfBitsFlagsNoXORClip = 0x00000004
        };
    
        private struct RtfFontFamilyDef
        {
            public const string Unknown = @"\fnil";
            public const string Roman = @"\froman";
            public const string Swiss = @"\fswiss";
            public const string Modern = @"\fmodern";
            public const string Script = @"\fscript";
            public const string Decor = @"\fdecor";
            public const string Technical = @"\ftech";
            public const string BiDirect = @"\fbidi";
        }
    
        [DllImport("gdiplus.dll")]
        private static extern uint GdipEmfToWmfBits(IntPtr _hEmf,
          uint _bufferSize, byte[] _buffer,
          int _mappingMode, EmfToWmfBitsFlags _flags);
    
    
        private string GetFontTable(Font font)
        {
            var fontTable = new StringBuilder();
            // Append table control string
            fontTable.Append(@"{\fonttbl{\f0");
            fontTable.Append(@"\");
            var rtfFontFamily = new HybridDictionary();
            rtfFontFamily.Add(FontFamily.GenericMonospace.Name, RtfFontFamilyDef.Modern);
            rtfFontFamily.Add(FontFamily.GenericSansSerif, RtfFontFamilyDef.Swiss);
            rtfFontFamily.Add(FontFamily.GenericSerif, RtfFontFamilyDef.Roman);
            rtfFontFamily.Add("UNKNOWN", RtfFontFamilyDef.Unknown);
    
            // If the font's family corresponds to an RTF family, append the
            // RTF family name, else, append the RTF for unknown font family.
            fontTable.Append(rtfFontFamily.Contains(font.FontFamily.Name) ? rtfFontFamily[font.FontFamily.Name] : rtfFontFamily["UNKNOWN"]);
            // \fcharset specifies the character set of a font in the font table.
            // 0 is for ANSI.
            fontTable.Append(@"\fcharset0 ");
            // Append the name of the font
            fontTable.Append(font.Name);
            // Close control string
            fontTable.Append(@";}}");
            return fontTable.ToString();
        }
    
        private string GetImagePrefix(Image _image)
        {
            float xDpi, yDpi;
            var rtf = new StringBuilder();
            using (Graphics graphics = CreateGraphics())
            {
                xDpi = graphics.DpiX;
                yDpi = graphics.DpiY;
            }
            // Calculate the current width of the image in (0.01)mm
            var picw = (int)Math.Round((_image.Width / xDpi) * 2540);
            // Calculate the current height of the image in (0.01)mm
            var pich = (int)Math.Round((_image.Height / yDpi) * 2540);
            // Calculate the target width of the image in twips
            var picwgoal = (int)Math.Round((_image.Width / xDpi) * 1440);
            // Calculate the target height of the image in twips
            var pichgoal = (int)Math.Round((_image.Height / yDpi) * 1440);
            // Append values to RTF string
            rtf.Append(@"{\pict\wmetafile8");
            rtf.Append(@"\picw");
            rtf.Append(picw);
            rtf.Append(@"\pich");
            rtf.Append(pich);
            rtf.Append(@"\picwgoal");
            rtf.Append(picwgoal);
            rtf.Append(@"\pichgoal");
            rtf.Append(pichgoal);
            rtf.Append(" ");
    
            return rtf.ToString();
        }
    
        private string getRtfImage(Image image)
        {
            // Used to store the enhanced metafile
            MemoryStream stream = null;
            // Used to create the metafile and draw the image
            Graphics graphics = null;
            // The enhanced metafile
            Metafile metaFile = null;
            try
            {
                var rtf = new StringBuilder();
                stream = new MemoryStream();
                // Get a graphics context from the RichTextBox
                using (graphics = CreateGraphics())
                {
                    // Get the device context from the graphics context
                    IntPtr hdc = graphics.GetHdc();
                    // Create a new Enhanced Metafile from the device context
                    metaFile = new Metafile(stream, hdc);
                    // Release the device context
                    graphics.ReleaseHdc(hdc);
                }
    
                // Get a graphics context from the Enhanced Metafile
                using (graphics = Graphics.FromImage(metaFile))
                {
                    // Draw the image on the Enhanced Metafile
                    graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height));
                }
    
                // Get the handle of the Enhanced Metafile
                IntPtr hEmf = metaFile.GetHenhmetafile();
                // A call to EmfToWmfBits with a null buffer return the size of the
                // buffer need to store the WMF bits.  Use this to get the buffer
                // size.
                uint bufferSize = GdipEmfToWmfBits(hEmf, 0, null, 8, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
                // Create an array to hold the bits
                var buffer = new byte[bufferSize];
                // A call to EmfToWmfBits with a valid buffer copies the bits into the
                // buffer an returns the number of bits in the WMF.  
                uint _convertedSize = GdipEmfToWmfBits(hEmf, bufferSize, buffer, 8, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
                // Append the bits to the RTF string
                foreach (byte t in buffer)
                {
                    rtf.Append(String.Format("{0:X2}", t));
                }
                return rtf.ToString();
            }
            finally
            {
                if (graphics != null)
                    graphics.Dispose();
                if (metaFile != null)
                    metaFile.Dispose();
                if (stream != null)
                    stream.Close();
            }
        }
    

    我建议你把它包装成你自己的UserControl

    【讨论】:

    • 这几乎是我的答案,但是当我以这种方式添加图像时,图像边缘的质量似乎有所下降。不确定您是否可以说出为什么会发生这种情况。示例imgur.com/6TCzKkv
    • 我发现如果我使用 bmp instread png 可以获得更好的质量
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-08
    相关资源
    最近更新 更多