richTextBox.DrawToBitmap не рисует содержащий текст?

Если у меня есть richTextBox и я запускаю DrawToBitmap, он не рисует текст внутри richTextBox.

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

Есть ли способ это исправить?

5 ответов

Решение

Эта тема заняла второе место в Google. Кажется, есть именно то, что вы хотите. Потому что я полагаю, что вы используете это внутри своей функции из этого вопроса Принятие элементов формы в качестве аргументов метода?, вероятно, лучше сделать что-то подобное.

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;
}

Я не скомпилировал это, и есть способ сделать это, не рискуя StackruException, но это должно помочь вам начать.

Я знаю, что это относительно старое, но рабочее решение, которое я нашел по адресу 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;
}

Из статьи библиотеки MSDN для RichTextBox.DrawToBitmap():

Этот метод не подходит для этого класса.

Неприличный способ сказать, что родной элемент управления Windows richedit не поддерживает WM_PRINT. Взять снимок экрана - вариант, Новиков дал вам ссылку на мой ответ.

Более того, более поздняя версия элемента управления 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;
         }
    }
}

Я нашел связанный ответ здесь: как напечатать содержимое текстового поля на любом устройстве с правильным форматированием?

Я изменил это, чтобы сделать мой закадровый 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);
    }

Я протестировал вышеперечисленные методы, и всякий раз, когда я загружаю сохраненное растровое изображение в ImageViewer(например, Paint), пользовательский интерфейс SaveFileDialog переходил в фон текста. К счастью, я нашел простое решение:

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 не влияет на процесс, потому что при нажатии Button или MenuStripItem чтобы сохранить, он обновит и перекрасит:)

Я реализовал try-catch метод, чтобы он перехватил ошибку, если она возникнет, а не приложение просто (Not Responding)

В catch метод - это Retry Button

Он обнаружит ошибку и предоставит вам выбор: Cancel целый Operation, или Retry

Я использовал goto чтобы иметь возможность просто перемотать назад и сделать еще одну попытку сохранить файл, вместо того, чтобы SaveFileDialog снова появиться.

Я надеюсь, это поможет вам:)

Другие вопросы по тегам