Detecting if paste event occurred inside a rich text box
Asked Answered
S

3

17

Is there a way by which we can find out if a clip board paste event occurred in a rich text box? This event would be used in order to do certain stuff, with the pasted block of text.

thanks

Here is my code

 protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        if (m.Msg == WM_PASTE)
        {
            OnPasteOccurred();
            MessageBox.Show("Pas");
        }
        if (m.Msg == 0x000F)
        {
            if (PaintControl)
            {
                base.WndProc(ref m);
            }
            else
            {
                m.Result = IntPtr.Zero;
            }
        }
        else
        {
            base.WndProc(ref m);
        }
    }

Edit

I wish to do some syntax highlighting or indentation based on paste events, something which this particular code editor seems to be doing very efficiently. I don't know how it is doing it. Would require help in this particular direction. I am pretty sure that there must some native Win32 code or something like that can be intercepted. I have tried tracking down keys, mouse events and it is not pretty.

Stokowski answered 11/4, 2011 at 7:32 Comment(4)
A quick web search reveals that RichTextBox doesn't handle paste events by sending WM_PASTE to itself. So I'm out of ideas.Fosterling
You may be able to wrap the control in your own custom class and override the Paste method. This is, of course, assuming that's the method being called on a paste.Changeling
Hmm, in .NET 2.0, you cannot override the paste() method, and probably the reason is given by @David above.Stokowski
Can't you just check if your rich text control has focus and if it has, then paste was in it?Nudibranch
S
20

It's a little bit tricky to detect a paste operation in the RichTextBox.

First solution may be to detect the WM_PASTE message overriding the WndProc but unfortunately the control doesn't send that message to itself when it performs a paste operation.

Naïve detection

To detect the keyboard events may work (you have to override the OnKeyDown function) then check if the key combinations (CTRL+V and SHIFT+INS). Something like this:

protected override OnKeyDown(KeyEventArgs e)
{
     bool ctrlV = e.Modifiers == Keys.Control && e.KeyCode == Keys.V;
     bool shiftIns = e.Modifiers == Keys.Shift && e.KeyCode == Keys.Insert;

     if (ctrlV || shiftIns)
         DoSomething();
}

It works well but you can't catch the paste operation made using the mouse (right click to open the context menu) and the paste operations made via drag & drop. If you do not need them you can use this solution (at least it's simply and straightforward).

Better detection

Assumption: when user types inside the RichTextBox he inserts one character per time. How can you use this? Well, when you detect a bigger change you detected a paste operation because user can't type more than once character per time (ok, you can argue that it's not always true because of Unicode surrogates). See also VB.NET version and more details about Unicode stuff.

private int _previousLength = 0;

private void richTextBox_TextChanged(object sender, EventArgs e)
{
   int currentLength = richTextBox.Text.Length;
   if (Math.Abs(currentLength - _previousLength) > 1)
      ProcessAllLines();

   _previousLength = currentLength;
}

Please note that you can't (because of how different IMEs work) use OnKeyDown (or similar). This works well only for western languages but it has problems with Unicode stuff (because, for example, String.Length property may be increased by two Char when user typed a single character. See also this post for much more details about this (well it's a strongly suggested reading even, even if - in this case - you don't care about it). In that post you'll also find code for a better algorithm to determine string length. In short you have to replace:

   int currentLength = richTextBox.Text.Length;

With this:

   int currentLength = StringInfo.GetTextElementEnumerator(richTextBox.Text)
       .Cast<string>()
       .Count();

After all this effort you may realize that...user can even paste a single character and it may go undetected. You're right, that's why this is a better detection instead of a perfect solution.

Perfect solution

The perfect solution (if you're running on Windows 8) of course exists, the native rich edit control sends an EN_CLIPFORMAT notification message. It's intended to notify a rich edit control's parent window that a paste occurred with a particular clipboard format. You can then override the WndProc of its parent to detect the WM_NOTIFY message for this notification. Anyway it's not few lines of code, check this MSDN article for details.

Swansdown answered 12/4, 2012 at 11:36 Comment(12)
isn't the msdn one applying to windows 8 only?Stokowski
@Stokowski sadly...yes, that's a notification message for Windows 8 only. I'll update my answer to make it clear!Swansdown
@Stokowski update: I guess a "true" syntax highligh algorithm won't suffer for poor performance 'cause he won't work on the full text and it'll do it in background on another thread while you're typing. Did you take a look to the code of the (old) SharpDevelop IDE? They did provide both colors and intellisense.Swansdown
Context menus aren't standard on the rich text box, though, and neither is drag and drop. You can perfectly control any pasting done through that. As far as I know only the shortcuts really need checking.Tonnie
@Tonnie yes, they're not enabled by default but they're one property away and in many cases any non trivial use will have something more. Also keyhandling is all but not trivial for IME of east languages (I do not mention tools to help visually/motor impaired people). I don't mean that you always need extra complexity, of course! If you have a very controlled solution then even simpler approach may work (note that keyboard shortcut may be disabled an manually handled).Swansdown
@AdrianoRepetti Actually, RichTextBox doesn't have a built-in right-click menu like TextBox does; you can just specify a context menu object to use. So you'd have to program it yourself anyway. Same for drag & drop; you'd need to filter out the type and handle everything yourself. In fact, my project has a RTB that only accepts file drops.Tonnie
@Tonnie for this question (syntax highlighting) context menu isn't built-in on RTF editor (code to do highlighting and code that adds UI would be pretty separate). Obviously yes, if you do everything by yourself (also disabling keyboard shortcuts) and you need this information on the same place then you don't need any notification from RichEdit itself!Swansdown
@AdrianoRepetti Given the fact RichTextBox has really simple Cut(), Copy() and Paste() functions for handling clipboard stuff anyway, just catching and overriding the paste shortcut exactly as you showed and ending the code with e.Handled=true; does seems like the simplest solution here in my opinion. Then you can do whatever you want with the pasted content.Tonnie
Note that I somehow agree with you, here it's just for sake of discussion. I avoid whenever possible to do it for two reasons: IMEs and tools for tools for visual/motor impaired people. Handling keydown/up is a pain with many IMEs (especially in far-east) and there are endless combinations you should test (I'm still searching an answer for this question).Swansdown
About other tools (also for voice dictation) I try to use standard behavior/implementation whenever possible: I can assume they extensively tested their products with standard controls but with my implementation it's an incognita...Swansdown
@AdrianoRepetti Ah, IMEs. Seems like a real pain. The closest I ever got to that mess is localization-specific upper/lower case rules. Godspeed on that -_-Tonnie
@Tonnie yes, it is! Actually I like that topic (self-advertising: https://mcmap.net/q/670436/-how-can-i-perform-a-unicode-aware-character-by-character-comparison-closed) but yes it's a source of thousands issues and headaches!Swansdown
L
1

Starting from .Net 3.0, there is a built-in method to detect the paste event:

DataObject.AddPastingHandler(this, OnPaste);

Just call this method in the constructor. If you want for example handle the paste event yourself as if the user entered the text manually, you can use

private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
    if (e.DataObject.GetDataPresent(typeof(string)))
    {
        var text = (string)e.DataObject.GetData(typeof(string));
        var composition = new TextComposition(InputManager.Current, this, text);
        TextCompositionManager.StartComposition(composition);
    }

    e.CancelCommand();
}
Lamori answered 14/10, 2014 at 8:56 Comment(1)
Isn't this WPF not winforms?Azpurua
T
0

I came across this old question and I would like to share my solution (it's VB but can it be easily converted). I use it to force paste as plain text when needed:

Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
    If ForcePasteAsPlainText And ((e.Control = True And e.KeyCode = Keys.V) Or (e.Shift = True And e.KeyCode = Keys.Insert)) Then
        MyBase.Paste(DataFormats.GetFormat(DataFormats.Text))
        e.Handled = True
        Return
    End If
    MyBase.OnKeyDown(e)
End Sub

Shadows Sub Paste()
    If ForcePasteAsPlainText Then
        MyBase.Paste(DataFormats.GetFormat(DataFormats.Text))
    Else
        MyBase.Paste()
    End If
End Sub

Shadows Sub Paste(clipFormat As DataFormats.Format)
    If ForcePasteAsPlainText Then
        MyBase.Paste(DataFormats.GetFormat(DataFormats.Text))
    Else
        MyBase.Paste(clipFormat)
    End If
End Sub
Twopenny answered 11/11, 2021 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.