Last active
April 22, 2024 09:32
-
-
Save KOZ60/f074e1424c1802ebca62fdfef7b3da7a to your computer and use it in GitHub Desktop.
RestrictText.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
using System.Windows.Forms; | |
public class RestrictText : TextBox | |
{ | |
private const char BACKSPACE = '\b'; // Backspace | |
private const char CTRL_A = '\x01'; // CTRL+A(Select All) | |
private const char CTRL_C = '\x03'; // CTRL+C(Copy) | |
private const char CTRL_V = '\x16'; // CTRL+V(Paste) | |
private const char CTRL_X = '\x18'; // CTRL+X(Cut) | |
private const char CTRL_Z = '\x1A'; // CTRL+Z(Undo) | |
private bool canRaiseEvents = true; | |
protected virtual bool IsAllowChar(char inputChar) { | |
if (!Multiline) { | |
if ("\r\n\t".IndexOf(inputChar) != -1) { | |
return false; | |
} | |
} | |
return true; | |
} | |
protected virtual bool IsValidText(string reflectText) { | |
foreach (char c in reflectText) { | |
if (!IsAllowChar(c)) { | |
return false; | |
} | |
} | |
return (MaxLength == 0 || reflectText.Length <= MaxLength); | |
} | |
protected override void OnReadOnlyChanged(EventArgs e) { | |
base.OnReadOnlyChanged(e); | |
if (ReadOnly) { | |
ClearUndo(); | |
} | |
} | |
protected override void OnKeyDown(KeyEventArgs e) { | |
bool valid = true; | |
switch (e.KeyCode) { | |
case Keys.Delete: | |
GetUnSelectedText(out string left, out string right); | |
if (SelectionLength > 0) { | |
valid = IsValidText(left + right); | |
} else { | |
if (right.Length > 0) { | |
valid = IsValidText(left + right.Substring(1)); | |
} | |
} | |
break; | |
} | |
if (!valid) { | |
e.Handled = true; | |
e.SuppressKeyPress = true; | |
return; | |
} | |
base.OnKeyDown(e); | |
} | |
protected void GetUnSelectedText(out string left, out string right) { | |
string prevText = base.Text; | |
left = prevText.Substring(0, SelectionStart); | |
right = prevText.Substring(SelectionStart + SelectionLength); | |
} | |
protected override void OnKeyPress(KeyPressEventArgs e) { | |
bool valid = true; | |
string left, right; | |
switch (e.KeyChar) { | |
case BACKSPACE: | |
GetUnSelectedText(out left, out right); | |
if (SelectionLength > 0) { | |
valid = IsValidText(left + right); | |
} else { | |
if (left.Length > 0) { | |
valid = IsValidText(left.Substring(0, left.Length - 1) + right); | |
} | |
} | |
break; | |
case CTRL_A: | |
case CTRL_C: | |
case CTRL_V: | |
case CTRL_X: | |
case CTRL_Z: | |
break; | |
default: | |
if (IsAllowChar(e.KeyChar)) { | |
GetUnSelectedText(out left, out right); | |
StringBuilder checkText = StringBuilderCache.Acquire(); | |
checkText.Append(left); | |
checkText.Append(e.KeyChar); | |
checkText.Append(right); | |
valid = IsValidText(StringBuilderCache.GetStringAndRelease(checkText)); | |
} else { | |
valid = false; | |
} | |
break; | |
} | |
if (!valid) { | |
e.Handled = true; | |
return; | |
} | |
base.OnKeyPress(e); | |
} | |
[DebuggerStepThrough] | |
protected override void WndProc(ref Message m) { | |
switch (m.Msg) { | |
case WM_CUT: | |
if (!ReadOnly) { | |
WmCut(ref m); | |
} | |
break; | |
case WM_CLEAR: | |
if (!ReadOnly) { | |
WmClear(ref m); | |
} | |
break; | |
case WM_COPY: | |
base.WndProc(ref m); | |
break; | |
case WM_PASTE: | |
if (!ReadOnly) { | |
WmPaste(ref m); | |
} | |
break; | |
case WM_UNDO: | |
case EM_UNDO: | |
if (!ReadOnly) { | |
WmUndo(ref m); | |
} | |
break; | |
default: | |
base.WndProc(ref m); | |
break; | |
} | |
} | |
private void WmCut(ref Message m) { | |
GetUnSelectedText(out string left, out string right); | |
if (IsValidText(left + right)) { | |
base.WndProc(ref m); | |
} | |
} | |
private void WmClear(ref Message m) { | |
GetUnSelectedText(out string left, out string right); | |
if (IsValidText(left + right)) { | |
base.WndProc(ref m); | |
} | |
} | |
private void WmPaste(ref Message m) { | |
if (!Clipboard.ContainsText()) { | |
return; | |
} | |
GetUnSelectedText(out string left, out string right); | |
StringBuilder pasteText = StringBuilderCache.Acquire(); | |
foreach (char c in Clipboard.GetText()) { | |
if (IsAllowChar(c)) { | |
StringBuilder checkText = StringBuilderCache.Acquire(); | |
checkText.Append(left); | |
checkText.Append(pasteText.ToString()); | |
checkText.Append(c); | |
checkText.Append(right); | |
if (IsValidText(StringBuilderCache.GetStringAndRelease(checkText))) { | |
pasteText.Append(c); | |
} | |
} | |
} | |
if (pasteText.Length > 0) { | |
Paste(StringBuilderCache.GetStringAndRelease(pasteText)); | |
} else { | |
StringBuilderCache.Release(pasteText); | |
} | |
m.Result = IntPtr.Zero; | |
} | |
private void WmUndo(ref Message m) { | |
LockWindowUpdate(m.HWnd); | |
try { | |
string prevText = base.Text; | |
base.WndProc(ref m); | |
if (!IsValidText(base.Text)) { | |
SetTextWithoutEvents(prevText); | |
} | |
} finally { | |
LockWindowUpdate(IntPtr.Zero); | |
Invalidate(); | |
} | |
} | |
protected override bool CanRaiseEvents { | |
get { | |
return canRaiseEvents; | |
} | |
} | |
protected void SetTextWithoutEvents(string value) { | |
if (base.Text != value) { | |
canRaiseEvents = false; | |
try { | |
base.Text = value; | |
} finally { | |
canRaiseEvents = true; | |
} | |
} | |
} | |
protected override void OnEnter(EventArgs e) { | |
OnBeforeEnter(e); | |
base.OnEnter(e); | |
} | |
protected virtual void OnBeforeEnter(EventArgs e) { | |
SetTextWithoutEvents(UnFormat(base.Text)); | |
SelectAll(); | |
} | |
protected override void OnLeave(EventArgs e) { | |
OnBeforeLeave(e); | |
base.OnLeave(e); | |
} | |
protected virtual void OnBeforeLeave(EventArgs e) { | |
SetTextWithoutEvents(Format(base.Text)); | |
} | |
protected virtual string UnFormat(string value) { | |
StringBuilder sb = StringBuilderCache.Acquire(); | |
foreach (char c in value) { | |
if (IsAllowChar(c)) { | |
sb.Append(c); | |
} | |
} | |
return StringBuilderCache.GetStringAndRelease(sb); | |
} | |
protected virtual string Format(string value) { | |
return value; | |
} | |
protected class StringBuilderCache | |
{ | |
private const int MAX_BUILDER_SIZE = 360; | |
private const int DefaultCapacity = 16; | |
[ThreadStatic] | |
private static StringBuilder CachedInstance; | |
public static StringBuilder Acquire(int capacity = DefaultCapacity) { | |
if (capacity <= MAX_BUILDER_SIZE) { | |
StringBuilder sb = CachedInstance; | |
if (sb != null) { | |
if (capacity <= sb.Capacity) { | |
CachedInstance = null; | |
sb.Clear(); | |
return sb; | |
} | |
} | |
} | |
return new StringBuilder(capacity); | |
} | |
public static void Release(StringBuilder sb) { | |
if (sb.Capacity <= MAX_BUILDER_SIZE) { | |
CachedInstance = sb; | |
} | |
} | |
public static string GetStringAndRelease(StringBuilder sb) { | |
string result = sb.ToString(); | |
Release(sb); | |
return result; | |
} | |
} | |
private static class ExternDll | |
{ | |
public const string User32 = "user32.dll"; | |
} | |
private const int | |
WM_CUT = 0x0300, | |
WM_COPY = 0x0301, | |
WM_PASTE = 0x0302, | |
WM_CLEAR = 0x0303, | |
WM_UNDO = 0x0304; | |
private const int | |
EM_UNDO = 0x00C7; | |
[DllImport(ExternDll.User32)] | |
private static extern bool LockWindowUpdate(IntPtr hWndLock); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment