【问题标题】:richTextBox.DrawToBitmap Does Not Draw Containing Text?richTextBox.DrawToBitmap 不绘制包含文本?
【发布时间】:2011-02-11 21:30:48
【问题描述】:

如果我有一个richTextBox 并在其上运行DrawToBitmap,它不会在richTextBox 内绘制任何文本。

Bitmap b = new Bitmap(rtb.Width, rtb.Height);
inputControl.DrawToBitmap(b, new Rectangle(0, 0, b.Width, b.Height));

有没有办法解决这个问题?

【问题讨论】:

    标签: c# controls drawtobitmap


    【解决方案1】:

    我知道这是比较老的,但我在http://www.windows-tech.info/3/8ffaf21eed5de2d4.php 找到了一个可行的解决方案:

    public static Bitmap RtbToBitmap(RichTextBox rtb)
    {
        rtb.Update(); // Ensure RTB fully painted
        Bitmap bmp = new Bitmap(rtb.Width, rtb.Height);
        using (Graphics gr = Graphics.FromImage(bmp))
        {
            gr.CopyFromScreen(rtb.PointToScreen(Point.Empty), Point.Empty, rtb.Size);
        }
        return bmp;
    }
    

    【讨论】:

    • 这对我有用;我正在使用 Windows 窗体。我有一个自定义透明面板,其下方带有富文本框,以便我可以在同一个“页面”上编写文本和绘图。绝对完美的解决方案!
    • 这不起作用...它绘制图像,但用它绘制 SaveFileDialog-UI。意思是,它会绘制文本,但是如果将其加载到 Paint 中,那么您将看到 SaveFileDialog-Window 淡入背景。看看我的回答:)
    • 不,我没有“添加我自己的废话”......当使用您的解决方案时,它也会用它绘制SaveFileDialog。我事先画了RichTextBox,这样它就不会用它画SaveFileDialog-UI。
    • @Momoro,是的,你做到了。在此之前调用我的函数,然后保存图像,您会看到保存文件对话框不是图像的一部分。再次:调用函数之前调用SaveFileDialog!!!
    【解决方案2】:

    This thread 在 Google 中排名第二。似乎有你想要的。因为我想你在这个问题Accepting Form Elements As Method Arguments? 的函数中使用它,所以最好做这样的事情。

    if(inputControl is RichTextBox)
    {
        //do specifc magic here
    }
    else
    {
        //general case
    }
    

    您可以递归地检查包含 RichTextBox 的控件

    bool ContainsOrIsRichTextBox(Control inputControl)
    {
        if(inputControl is RichTextBox) return true;
        foreach(Control control in inputControl.Controls)
        {
            if(ContainsOrIsRichTextBox(control)) return true;
        }
        return false;
    }
    

    我还没有编译这个,有一种方法可以做到这一点而不会冒 StackOverflowException 的风险,但这应该可以帮助你开始。

    【讨论】:

    • 假设我发送了一个包含richTextBox 的面板。有没有办法检查面板是否包含richTextBox?
    • 您必须遍历控件树。我提供了一个 sn-p。
    【解决方案3】:

    来自 RichTextBox.DrawToBitmap() 的 MSDN 库文章:

    此方法与此类无关。

    说本机 Windows Richedit 控件不支持 WM_PRINT 是一种糟糕的说法。截图是一种选择,Novikov 给了你我的答案的链接。

    【讨论】:

      【解决方案4】:

      值得一提的是,RichTextBox 控件的更高版本正确地支持了 DrawToBitmap 方法;它还提高了性能并具有更多功能。

      internal class RichTextBox5: RichTextBox
      {
          [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
          static extern IntPtr LoadLibrary(string lpFileName);
      
          protected override CreateParams CreateParams
          {
              get
              {
                 CreateParams cparams = base.CreateParams; 
                 if (LoadLibrary("msftedit.dll") != IntPtr.Zero)
                 {
                    cparams.ClassName = "RICHEDIT50W";
                 }
                 return cparams;
               }
          }
      }
      

      【讨论】:

        【解决方案5】:

        我在这里找到了相关答案:how to print Rich text box contents on any device contenxt with proper formatting?

        我将其更改为将屏幕外的 RichTextBox 渲染为位图。这样我就可以在屏幕外创建一个位图,然后将其发送到 OpenGL。

            // Convert the unit used by the .NET framework (1/100 inch) 
            // and the unit used by Win32 API calls (twips 1/1440 inch)
            private const double anInch = 14.4;
        
            [StructLayout(LayoutKind.Sequential)]
            private struct RECT
            {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }
        
            [StructLayout(LayoutKind.Sequential)]
            private struct CHARRANGE
            {
                public int cpMin;               // First character of range (0 for start of doc)
                public int cpMax;               // Last character of range (-1 for end of doc)
            }
        
            [StructLayout(LayoutKind.Sequential)]
            private struct FORMATRANGE
            {
                public IntPtr    hdc;           // Actual DC to draw on
                public IntPtr    hdcTarget;     // Target DC for determining text formatting
                public RECT      rc;            // Region of the DC to draw to (in twips)
                public RECT      rcPage;        // Region of the whole DC (page size) (in twips)
                public CHARRANGE chrg;          // Range of text to draw (see earlier declaration)
            }
        
            private const int WM_USER        = 0x0400;
            private const int EM_FORMATRANGE = WM_USER + 57;
        
            [DllImport("USER32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
        
            /// <summary>
            /// Render the specified RichTextBox onto the specified bitmap
            /// </summary>
            /// <param name="textBox">RichTextBox to render</param>
            /// <param name="bitmap">Bitmap to render the RichTextBox onto</param>
            public void RenderToBitmap(RichTextBox textBox, Bitmap bitmap)
            {
                // Set area to render to be entire bitmap
                RECT rect;
                rect.Left   = 0;
                rect.Top    = 0;
                rect.Right  = (int)(bitmap.Width  * anInch);
                rect.Bottom = (int)(bitmap.Height * anInch);
        
                Graphics g   = Graphics.FromImage(bitmap);
                IntPtr   hdc = g.GetHdc();
        
                FORMATRANGE fmtRange;
                fmtRange.chrg.cpMin = textBox.GetCharIndexFromPosition(new Point(0,0));
                fmtRange.chrg.cpMax = textBox.GetCharIndexFromPosition(new Point(bitmap.Width,bitmap.Height));
                fmtRange.hdc        = hdc;                  // Use the same DC for measuring and rendering
                fmtRange.hdcTarget  = hdc;
                fmtRange.rc         = rect;
                fmtRange.rcPage     = rect;
        
                IntPtr lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
                Marshal.StructureToPtr(fmtRange, lparam, false);
        
                // Render the control to the bitmap
                SendMessage(textBox.Handle, EM_FORMATRANGE, new IntPtr(1), lparam);
        
                // Clean up
                Marshal.FreeCoTaskMem(lparam);
                g.ReleaseHdc(hdc);
            }
        

        【讨论】:

        • 有趣的事情。 4年后我在这里寻找如何做到这一点,我发现了什么?我在这里找到了我自己对这个问题的答案。我什至不记得写过这段代码!更有趣的是,我发现下面的答案是一种更好的方法。
        • 我正在尝试使用此方法,但遇到了一些问题。我想将位图保存到文件中,但 VS 抛出错误“无法将类型 'void' 转换为类型 '位图'' - 有解决办法吗?谢谢:)
        【解决方案6】:

        我测试了上面的方法,每当我将保存的位图加载到 ImageViewer(Like Paint) 中时,SaveFileDialog-UI 都会淡入文本背景。幸运的是,我找到了一个简单的解决方法:

        SaveFileDialog bfsd = new SaveFileDialog();       
        var rtb = richTextBox1;
        
                bfsd.Filter = "Bitmap (*.bmp)|*.bmp|All Files (*.*)|*.*";
                bfsd.Title = "Save your text as a Bitmap File";
        
                rtb.Update(); // Ensure RTB fully painted
                Bitmap bmp = new Bitmap(rtb.Width, rtb.Height);
                using (Graphics gr = Graphics.FromImage(bmp))
                {
                    gr.CopyFromScreen(rtb.PointToScreen(Point.Empty), Point.Empty, rtb.Size);
                }
        
                if (bfsd.ShowDialog()==DialogResult.OK)
                {
                 Draw:
                    try
                    {
                        bmp.Save(bfsd.FileName);
        
                        bmp.Dispose();
                    }
                    catch (Exception)
                    {
                        DialogResult dr = MessageBox.Show("An error ocurred while attempting to save your Image...", "Error! Error!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
        
                        if (dr == DialogResult.Retry)
                        {
                            goto Draw;
                        }
                        else if (dr == DialogResult.Cancel)
                        {
                            return;
                        }
                    }
        
        • 这样,它甚至会在您按下Save 之前绘制图片(别担心,在您按下Save 之前它不会真正保存图像

        Cancel 不会影响该过程,因为当您按ButtonMenuStripItem 保存它时,它会更新并重新绘制它:)

        我实现了一个try-catch 方法,这样它会在发生错误时捕获错误,而不是应用程序只是(Not Responding)

        catch 方法是 Retry Button

        它将捕获错误,并让您选择Cancel 整个OperationRetry

        我使用goto 来回退并再次尝试保存文件,而不是让SaveFileDialog 再次出现。

        希望对你有帮助:)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-02-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-09-28
          • 2017-01-11
          • 2014-09-28
          相关资源
          最近更新 更多