hook on default Paste event of WinForms TextBox control
While I would normally not suggest dropping to low level Windows API, and this may not be the only way of doing this, it does do the trick:
using System;
using System.Windows.Forms;
public class ClipboardEventArgs : EventArgs
{
public string ClipboardText { get; set; }
public ClipboardEventArgs(string clipboardText)
{
ClipboardText = clipboardText;
}
}
class MyTextBox : TextBox
{
public event EventHandler<ClipboardEventArgs> Pasted;
private const int WM_PASTE = 0x0302;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PASTE)
{
var evt = Pasted;
if (evt != null)
{
evt(this, new ClipboardEventArgs(Clipboard.GetText()));
// don't let the base control handle the event again
return;
}
}
base.WndProc(ref m);
}
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var tb = new MyTextBox();
tb.Pasted += (sender, args) => MessageBox.Show("Pasted: " + args.ClipboardText);
var form = new Form();
form.Controls.Add(tb);
Application.Run(form);
}
}
Ultimately the WinForms toolkit is not very good. It is a thin-ish wrapper around Win32 and the Common Controls. It exposes the 80% of the API that is most useful. The other 20% is often missing or not exposed in a way that is obvious. I would suggest moving away from WinForms and to WPF if possible as WPF seems to be a better architected framework for .NET GUIs.
Override Paste Into TextBox
That's possible, you can intercept the low-level Windows message that the native TextBox
control gets that tells it to paste from the clipboard. The WM_PASTE
message. Generated both when you press Ctrl+V with the keyboard or use the context menu's Paste command. You catch it by overriding the control's WndProc()
method, performing the paste as desired and not pass it on to the base class.
Add a new class to your project and copy/paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form, replacing the existing one.
using System;
using System.Windows.Forms;
class MyTextBox : TextBox {
protected override void WndProc(ref Message m) {
// Trap WM_PASTE:
if (m.Msg == 0x302 && Clipboard.ContainsText()) {
this.SelectedText = Clipboard.GetText().Replace('\n', ' ');
return;
}
base.WndProc(ref m);
}
}
Detecting if paste event occurred inside a rich text box
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.
Winforms textbox paste unreliable?
I have a fancy firewall that is also able block applications from looking at data in the clipboard, on per-application basis.
It does not prevent writing; the point of the feature is to stop malware from stealing important information that might end up in the clipboad, such as a password.
It can also block any given application from doing various other system tasks, e.g. taking a screenshot or navigating to a web page with the default system browser.
The customer might have something like that installed, with Notepad allowed in the rules.
Pasting of serialnumber over multiple textfields
In the first textbox, I would put a large limit.
On the 'text changed', check the length. If the change is greater than 4 (your maximum). Delete the extra stuff and spread it over your textboxes.
If you copy-paste, it'll text change of 32, and it would work. You could also change the cursor (I think its .Focus() but I could be wrong) so it automatically 'hops' between the boxes.
Block Paste event in a DomainUpDown control
Using a NativeWindow
you can handle messages of the inner TextBox
which is used in DomainUpDown
and NumericUpDown
controls.
In the follwowing code, I've handled WM_PASTE
method and just played a beep in response:
using System;
using System.Windows.Forms;
public class MyDomainUpDown : DomainUpDown
{
MyWindoHelper wh;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
wh = new MyWindoHelper(Controls[1]);
}
protected override void Dispose(bool disposing)
{
if (wh != null)
wh.DestroyHandle();
base.Dispose(disposing);
}
class MyWindoHelper : NativeWindow
{
Control c; //For future reference if needed.
public MyWindoHelper(Control control)
{
c = control;
this.AssignHandle(c.Handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg != 0x0302 /*WM_PASTE*/)
base.WndProc(ref m);
else
System.Media.SystemSounds.Beep.Play();
}
}
}
If you may want to validate the input and paste a sanitized text to the control, take a look at the following post:
- Textbox - Validate input before paste
Winforms MaskedTextBox - Reformatting pasted text to match mask
While this is a hammer solution, there are limitations to the mask string and i don't see another way around it. What you need is to capture the paste event and process the text before it gets in the textbox. See below a simplistic example
class MyMaskedTextbox : MaskedTextBox
{
const int WM_PASTE = 0x0302;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PASTE:
if (Clipboard.ContainsText())
{
string text = Clipboard.GetText();
text = text.Replace(' ', '-');
//put your processing here
Clipboard.SetText(text);
}
break;
}
base.WndProc(ref m);
}
}
detect enterline when copy big text data into textbox
you can create hook on default “Paste” event of WinForms TextBox control like below by creating custom textbox specially for your requirement.
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class MyTextBox : TextBox
{
protected override void WndProc(ref Message m)
{
// Trap WM_PASTE:
if (m.Msg == 0x302 && Clipboard.ContainsText())
{
var pastText = Clipboard.GetText().Replace('\n', ' ');
if (pastText.Length > MaxLength)
{
//Do Something
}
else
{
//Do Something
}
this.SelectedText = pastText;
return;
}
base.WndProc(ref m);
}
}
}
Related Topics
Does Using Parameterized SQLcommand Make My Program Immune to SQL Injection
How to Implement Automatic Sorting of Datagridview
Sending an Array of Values to Oracle Procedure to Use in Where in Clause
Visual Studio Displaying Errors Even If Projects Build
Get User Location by Ip Address
Why Use Asqueryable() Instead of List()
Dependency Injection VS Service Location
Can You Create SQL Views/Stored Procedure Using Entity Framework 4.1 Code First Approach
Casting VS Converting an Object Tostring, When Object Really Is a String
Creating Directories in a Ziparchive C# .Net 4.5
Possible Pitfalls of Using This (Extension Method Based) Shorthand
Get Values from Process Standardoutput
What Does "Yield Break;" Do in C#
Difference Between System.Datetime.Now and System.Datetime.Today
ASP.NET MVC - How to Show Unauthorized Error on Login Page
Having the Output of a Console Application in Visual Studio Instead of the Console