Skip to content

Instantly share code, notes, and snippets.

@KOZ60
Last active April 22, 2024 09:32
Show Gist options
  • Save KOZ60/f074e1424c1802ebca62fdfef7b3da7a to your computer and use it in GitHub Desktop.
Save KOZ60/f074e1424c1802ebca62fdfef7b3da7a to your computer and use it in GitHub Desktop.
RestrictText.cs
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