2
votes

I need to use a RichTextBox, not a normal textbox because of the way it keeps the caret position, from line to line. But I need to keep the text in the same font all the time even if it is pasted.

At the moment I have it selecting the entire text and changing the font to the original (Lucida Console) but it look horrible when you paste into it as it flashes blue.

3

3 Answers

4
votes

If you are handling the pasting programatically don't use the Paste method. Instead use Clipboard.GetDataObject().GetData(DataFormats.Text) to get the text in a string and then add the text using the Rtf or Text property to the RichTextBox:

string s = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
richTextBox.Text += s;

Otherwise you could handle the Ctrl+V key press:

void RichTextBox1_KeyDown(object sender, KeyEventArgs e)
{
    if(e.Control == true && e.KeyCode == Keys.V)
    {
        string s = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
        richTextBox.Text += s;
        e.Handled = true; // disable Ctrl+V
    }
}
2
votes

Darin's method ignores caret position and always appends to the end of text.

Actually, there are better method. Use overload of RichTextBox.Paste():

DataFormats.Format plaintext_format = DataFormats.GetFormat(DataFormats.Text);
this.Paste(plaintext_format);

Works like charm for me.

0
votes

Both @Darin & @idn's answers are good, however I could get neither to work when pasting the following rich text:

   This is text after an arrow.
This is a new line

The font would always change to WingDings. I had copied this from MS Word:

Text copied from MS Word

Specifically, the plain-text format method described by @idn above did indeed just paste plain text, but something was happening in which the font was changed too.

The following code handles the KeyUp event to just select all text and replace its original colours and font (i.e. formatting). To ensure that this isn't visible on the screen as a flicker, a special method of disabling window repaint events was employed. Control draw disablement occurs in the KeyDown event, the RichTextBox control handles the paste event by itself, and then Control drawing is re-enabled at the end. Finally, this only happens for CTL+V and SHIFT+INS, both of which are standard paste commands:

    /// <summary>
    /// An application sends the WM_SETREDRAW message to a window to allow changes in that 
    /// window to be redrawn or to prevent changes in that window from being redrawn.
    /// </summary>
    private const int WM_SETREDRAW = 11; 

    private void txtRichTextBox_KeyDown(object sender, KeyEventArgs e)
    {
        // For supported Paste key shortcut combinations, suspend painting
        // of control in preparation for RTF formatting updates on KeyUp
        if ((e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.V) || // CTL+V
            (!e.Control && e.Shift && !e.Alt && e.KeyCode == Keys.Insert)) // SHIFT+INS
        {
            // Send Suspend Redraw message to avoid flicker. Drawing is 
            // restored in txtRichTextBox_KeyUp event handler
            // [this.SuspendLayout() doesn't work properly] 
            Message msgSuspendUpdate = Message.Create(
                txtRichTextBox.Handle, WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
            NativeWindow window = NativeWindow.FromHandle(txtRichTextBox.Handle);
            window.DefWndProc(ref msgSuspendUpdate);
        }
    }

    private void txtRichTextBox_KeyUp(object sender, KeyEventArgs e)
    {
        // Following supported Paste key shortcut combinations, restore
        // original formatting, then resume painting of control.
        if ((e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.V) || // CTL+V
            (!e.Control && e.Shift && !e.Alt && e.KeyCode == Keys.Insert)) // SHIFT+INS
        {
            // Layout already suspended during KeyDown event

            // Capture cursor position. Cursor will later be placed 
            // after inserted text
            int selStart = txtRichTextBox.SelectionStart;
            int selLen = txtRichTextBox.SelectionLength;

            // Replace all text with original font & colours
            txtRichTextBox.SelectAll();
            txtRichTextBox.SelectionFont = txtRichTextBox.Font;
            txtRichTextBox.SelectionColor = txtRichTextBox.ForeColor;
            txtRichTextBox.SelectionBackColor = txtRichTextBox.BackColor;

            // Restore original selection
            txtRichTextBox.SelectionStart = selStart;
            txtRichTextBox.SelectionLength = selLen;

            txtRichTextBox.ScrollToCaret();

            // Resume painting of control
            IntPtr wparam = new IntPtr(1); // Create a C "true" boolean as an IntPtr
            Message msgResumeUpdate = Message.Create(
                txtRichTextBox.Handle, WM_SETREDRAW, wparam, IntPtr.Zero);
            NativeWindow window = NativeWindow.FromHandle(txtRichTextBox.Handle);
            window.DefWndProc(ref msgResumeUpdate);
            txtRichTextBox.Invalidate();
            txtRichTextBox.Refresh();
        }
    }

A caveat of this approach is that, because the events are not suppressed (e.Handled = true;), the standard CTL+Z (undo) operation is supported. However this process cycles through undoing the format changes too. I don't see this as a big problem, because the next time that text is pasted, formatting is once again removed.

This approach isn't perfect, because if the text is copied and pasted from the RichTextBox (into another application), the newly applied formatting remains, but in my opinion, that's better than losing the undo functionality. If the undo functionality isn't important, then replace the text selection and formatting application with a replacement of the text to remove all formatting, as per this answer: https://stackoverflow.com/a/1557270/3063884

var t = txtRichTextBox.Text;
txtRichTextBox.Text = t;