Disable spacekey textbox in c# wpf - c#

I have textbox that need to allow only numbers, delete and backsapce. This code works for this, but its allowing space key even though I disabled it. I'm not sure what is missing here.
public class NumberTextBox : TextBox
{
/// <summary>
/// Allows only numbers, delete, backspace keys
/// </summary>
public NumberTextBox()
{
KeyDown += new KeyEventHandler(OnKeyDown);
}
private bool IsNumberKey(Key inKey)
{
if (inKey < Key.D0 || inKey > Key.D9)
{
if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
{
return false;
}
}
return true;
}
private bool IsActionKey(Key inKey)
{
return inKey == Key.Delete || inKey == Key.Back;
}
private bool IsSpaceKey(Key inKey)
{
if (inKey == Key.Space)
{
return true;
}
return false;
}
protected void OnKeyDown(object sender, KeyEventArgs e)
{
e.Handled = !IsNumberKey(e.Key) && !IsActionKey(e.Key) && IsSpaceKey(e.Key);
}
}

From the microsoft website:
public class NumericTextBox : TextBox
{
bool allowSpace = false;
// Restricts the entry of characters to digits (including hex), the negative sign,
// the decimal point, and editing keystrokes (backspace).
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
NumberFormatInfo numberFormatInfo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
string decimalSeparator = numberFormatInfo.NumberDecimalSeparator;
string groupSeparator = numberFormatInfo.NumberGroupSeparator;
string negativeSign = numberFormatInfo.NegativeSign;
string keyInput = e.KeyChar.ToString();
if (Char.IsDigit(e.KeyChar))
{
// Digits are OK
}
else if (keyInput.Equals(decimalSeparator) || keyInput.Equals(groupSeparator) ||
keyInput.Equals(negativeSign))
{
// Decimal separator is OK
}
else if (e.KeyChar == '\b')
{
// Backspace key is OK
}
// else if ((ModifierKeys & (Keys.Control | Keys.Alt)) != 0)
// {
// // Let the edit control handle control and alt key combinations
// }
else if (this.allowSpace && e.KeyChar == ' ')
{
}
else
{
// Swallow this invalid key and beep
e.Handled = true;
// MessageBeep();
}
}
public int IntValue
{
get
{
return Int32.Parse(this.Text);
}
}
public decimal DecimalValue
{
get
{
return Decimal.Parse(this.Text);
}
}
public bool AllowSpace
{
set
{
this.allowSpace = value;
}
get
{
return this.allowSpace;
}
}
}

Related

I am trying to make undo/redo function to a textbox using stack but it only undo one character at a time I want to undo a string

I am trying to make an undo/redo feature on a textbox in Winforms, I found a code in here that works but it only undo one character at a time. I was hoping to undo a whole string, can someone help me or give me an idea how? I'm new at data structure and don't really know how to start.
Here's the code:
Stack<Func<object>> undoStack = new Stack<Func<object>>();
Stack<Func<object>> redoStack = new Stack<Func<object>>();
public LowQuantityProd()
{
InitializeComponent();
textBox1.KeyDown += TextBox1_KeyDown;
}
private void TextBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ControlKey && ModifierKeys == Keys.Control) { }
else if (e.KeyCode == Keys.U && ModifierKeys == Keys.Control)
{
if (undoStack.Count > 0)
{
StackPush(sender, redoStack);
undoStack.Pop()();
}
}
else if (e.KeyCode == Keys.R && ModifierKeys == Keys.Control)
{
if (redoStack.Count > 0)
{
StackPush(sender, undoStack);
redoStack.Pop()();
}
}
else
{
redoStack.Clear();
StackPush(sender, undoStack);
}
}
private void StackPush(object sender, Stack<Func<object>> stack)
{
TextBox textBox = (TextBox)sender;
var tBT = textBox.Text(textBox.Text, textBox.SelectionStart);
stack.Push(tBT);
}
}
public static class Extensions
{
public static Func<TextBox> Text(this TextBox textBox, string text, int sel)
{
return () =>
{
textBox.Text = text;
textBox.SelectionStart = sel;
return textBox;
};
}
}

Why does TextBox.GetFirstCharIndexFromLine always return 0?

I made a subclass for TextBox and tested the following method in a separate test project.
internal static int NumberOfPhysicalLinesInTextBox(TextBox tb)
{
int lc = 0;
while (tb.GetFirstCharIndexFromLine(lc) != -1)
{
++lc;
}
return lc;
}
The code Works well, but in my subclass it does not. The above static method is called only from the method UpdateVisibleScrollBars, which is called only in the following places:
from the subclass' c-tor
OnTextChanged
OnFontChanged
OnResize
The only speciality of this subclass is that it has a placeholder when the user did not enter anything in the TextBox, and this UpdateVisibleScrollBars. In this subclass NumberOfPhysicalLinesInTextBox does not return, it loops indefinitely because the GetFirstCharIndexFromLine always returns 0 when the text is the placeholder: "Enter text here...".
Update: I do not use Lines because I need the physical lines (the lines that result after Word-wrapping), so I can know if I need to show or hide the vertical scrollbar. The TextBox is set with WordWrap = true. Here is the GetFirstCharIndexFromLine method's official documentation.
Update 2: All the class' code is below (without non-English comments):
class EnhancedTextBox : TextBox
{
internal string PlaceholderText = "Enter text here...";
internal string ActualText
{
get
{
return PlaceholderShown ? "" : Text;
}
set
{
if (value == "" || value == null)
{
if (Text == PlaceholderText)
{
PlaceholderShown = true;
ActualTextChanged?.Invoke(this, EventArgs.Empty);
}
else
{
if (!Focused)
{
BeforeActualTextChanged?.Invoke(this, EventArgs.Empty);
ProgrammaticTextChange = true;
Text = PlaceholderText;
ProgrammaticTextChange = false;
PlaceholderShown = true;
ActualTextChanged?.Invoke(this, EventArgs.Empty);
}
else
{
PlaceholderShown = false;
ActualTextChanged?.Invoke(this, EventArgs.Empty);
}
}
}
else
{
if (Text != value)
{
BeforeActualTextChanged?.Invoke(this, EventArgs.Empty);
ProgrammaticTextChange = true;
Text = value;
ProgrammaticTextChange = false;
}
PlaceholderShown = false;
ActualTextChanged?.Invoke(this, EventArgs.Empty);
}
}
}
internal Color _PlaceholderForeColor = Utils.GrayByPercent(50);
internal Color PlaceholderForeColor
{
get
{
return _PlaceholderForeColor;
}
set
{
if (_PlaceholderForeColor != value)
{
_PlaceholderForeColor = value;
Invalidate();
}
}
}
internal Color _NormalForeColor = Color.Empty;
internal Color NormalForeColor
{
get
{
return _NormalForeColor;
}
set
{
if (_NormalForeColor != value)
{
_NormalForeColor = value;
Invalidate();
}
}
}
internal bool _PlaceholderShown = true;
internal bool PlaceholderShown
{
get
{
return _PlaceholderShown;
}
set
{
if (_PlaceholderShown != value)
{
_PlaceholderShown = value;
ForceUpdatePlaceholderShown(value);
}
}
}
internal void ForceUpdatePlaceholderShown(bool value)
{
ForeColor = value ? PlaceholderForeColor :
NormalForeColor;
Invalidate();
}
public EnhancedTextBox() : base()
{
NormalForeColor = ForeColor;
ProgrammaticTextChange = true;
Text = PlaceholderText;
ProgrammaticTextChange = false;
PlaceholderShown = true;
ForceUpdatePlaceholderShown(true);
UpdateVisibleScrollBars();
}
protected override void OnEnter(EventArgs e)
{
HidePlaceholder();
base.OnEnter(e);
}
private void HidePlaceholder()
{
if (PlaceholderShown)
{
ProgrammaticTextChange = true;
Text = "";
ProgrammaticTextChange = false;
PlaceholderShown = false;
}
}
protected override void OnLeave(EventArgs e)
{
ShowPlaceholder();
base.OnLeave(e);
}
private void ShowPlaceholder()
{
if (Text == "")
{
ProgrammaticTextChange = true;
Text = PlaceholderText;
ProgrammaticTextChange = false;
PlaceholderShown = true;
}
}
internal static int NumberOfPhysicalLinesInTextBox(TextBox tb)
{
int lc = 0;
while (tb.GetFirstCharIndexFromLine(lc) != -1)
{
++lc;
}
return lc;
}
internal bool ProgrammaticTextChange = false;
/// <summary>
/// Do not use this event using handlers. Use ActualTextChanged instead.
/// </summary>
/// <param name="e"></param>
protected override void OnTextChanged(EventArgs e)
{
if (ProgrammaticTextChange)
{
return;
}
ActualText = Text;
base.OnTextChanged(e);
UpdateVisibleScrollBars();
}
private bool busy = false;
private void UpdateVisibleScrollBars()
{
if (busy) return;
busy = true;
bool c1 = false; // chars == Text.Length; // TODO: this not working for WordWrap = false
bool c2 = NumberOfPhysicalLinesInTextBox(this) > 2;
if (c1 && c2)
{
ScrollBars = ScrollBars.Both;
}
else if (c1)
{
ScrollBars = ScrollBars.Horizontal;
}
else if (c2)
{
ScrollBars = ScrollBars.Vertical;
}
else
{
ScrollBars = ScrollBars.None;
}
ScrollToCaret();
busy = false;
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
UpdateVisibleScrollBars();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
UpdateVisibleScrollBars();
}
public event EventHandler ActualTextChanged, BeforeActualTextChanged;
}
If I replace bool c2 = NumberOfPhysicalLinesInTextBox(this) > 2; with bool c2 = false; there is no non-ending while loop, although I see that the OnResize handler is called often in debugging with a breakpoint put on the c2 line, and repeatedly clicking Continue. Then If I press Continue really fast a few times, the program starts and is usable.
Update 3: Commenting out the UpdateVisibleScrollBars call inside the OnResize handler makes everything work. How can I make the scrollbars' visibility be changed when the TextBox is just resized?
Current code:
class EnhancedTextBox : TextBox
{
internal string _PlaceholderText = "Enter text here...";
internal string PlaceholderText
{
get
{
return _PlaceholderText;
}
set
{
_PlaceholderText = value;
Invalidate();
}
}
internal Color _PlaceholderForeColor = SystemColors.GrayText;
public Color PlaceholderForeColor
{
get
{
return _PlaceholderForeColor;
}
set
{
_PlaceholderForeColor = value;
Invalidate();
}
}
[Obsolete]
internal string ActualText
{
get
{
return Text;
}
set
{
if (Text != value)
{
Text = value;
}
}
}
internal Color _NormalForeColor = Color.Empty;
internal Color NormalForeColor
{
get
{
return _NormalForeColor;
}
set
{
if (_NormalForeColor != value)
{
_NormalForeColor = value;
ForeColor = value;
}
}
}
public EnhancedTextBox() : base()
{
NormalForeColor = ForeColor;
WordWrap = true;
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0xf)
{
if (!this.Focused && string.IsNullOrEmpty(this.Text)
&& !string.IsNullOrEmpty(this.PlaceholderText))
{
using (var g = this.CreateGraphics())
{
TextRenderer.DrawText(g, this.PlaceholderText, this.Font,
this.ClientRectangle, this.PlaceholderForeColor, this.BackColor,
TextFormatFlags.Top | TextFormatFlags.Left);
}
}
}
}
internal static int NumberOfPhysicalLinesInTextBox(TextBox tb)
{
int lc = 0;
while (tb.GetFirstCharIndexFromLine(lc) != -1)
{
++lc;
}
return lc;
}
internal bool ProgrammaticTextChange = false;
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
ActualTextChanged?.Invoke(this, e);
UpdateVisibleScrollBars();
}
private bool busy = false;
private Size textSize = Size.Empty;
private void UpdateVisibleScrollBars()
{
if (busy) return;
busy = true;
bool c1 = false; // chars == Text.Length; // TODO: this not working for WordWrap = false
bool c2 = NumberOfPhysicalLinesInTextBox(this) > 2;
if (c1 && c2)
{
ScrollBars = ScrollBars.Both;
}
else if (c1)
{
ScrollBars = ScrollBars.Horizontal;
}
else if (c2)
{
ScrollBars = ScrollBars.Vertical;
}
else
{
ScrollBars = ScrollBars.None;
}
ScrollToCaret();
busy = false;
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
UpdateVisibleScrollBars();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
//UpdateVisibleScrollBars();
}
[Obsolete]
public event EventHandler ActualTextChanged;
}

Xamarin forms android - Set floating label color when binding IsEnabled

I extend an entry and i would like that it looked like this :Entry enable and disable
Here is the renderer for android :
protected override void OnElementChanged(ElementChangedEventArgs<TextBox> e)
{
base.OnElementChanged(e);
if (e.OldElement != null) this.SetNativeControl(null);
if (e.NewElement == null) return;
if (this.Control == null)
{
var layout = this.CreateNativeControl();
this.editText.AddTextChangedListener(this);
this.editText.ImeOptions = ImeAction.Done;
if (this.Element.MaxLength != 0)
this.editText.SetFilters(new IInputFilter[] { new InputFilterLengthFilter(this.Element.MaxLength) });
this.SetNativeControl(layout);
}
this.ApplyEnabled();
this.ApplyErrorText();
this.ApplyPlaceholder();
this.ApplyText();
this.ApplyKeyboard();
this.ApplyIsWrapping();
this.ApplyBoxTextColor();
this.SetHintLabelActiveColor();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(this.Element.Placeholder))
this.ApplyPlaceholder();
else if (e.PropertyName == nameof(this.Element.IsInputValid) || e.PropertyName == nameof(this.Element.IsEnabled) || e.PropertyName == nameof(this.Element.IsMandatory))
this.ApplyEnabled();
else if (e.PropertyName == nameof(this.Element.ErrorText))
this.ApplyErrorText();
else if (e.PropertyName == nameof(this.Element.Placeholder))
this.ApplyPlaceholder();
else if (e.PropertyName == nameof(this.Element.Text))
this.ApplyText();
else if (e.PropertyName == nameof(this.Element.IsWrapping))
this.ApplyIsWrapping();
else if (e.PropertyName == nameof(this.Element.Keyboard))
this.ApplyKeyboard();
else if (e.PropertyName == nameof(this.Element.TextColor))
this.ApplyBoxTextColor();
}
private void SetHintLabelDefaultColor(Color color)
{
var hint = this.textInputLayout.Class.GetDeclaredField("mDefaultTextColor");
hint.Accessible = true;
hint.Set(this.textInputLayout, new ColorStateList(new int[][] { new[] { 0 } }, new int[] { color }));
}
private void SetHintLabelActiveColor()
{
var hintText = this.textInputLayout.Class.GetDeclaredField("mFocusedTextColor");
hintText.Accessible = true;
hintText.Set(this.textInputLayout, new ColorStateList(new int[][] { new[] { 0 } }, new int[] { this.Element.FloatingLabelColor.ToAndroid() }));
}
private void ApplyText()
{
if (this.textInputLayout.EditText == null || this.textInputLayout.EditText.Text == this.Element.Text)
return;
this.textInputLayout.EditText.Text = this.Element.Text;
}
private void ApplyBoxTextColor()
{
this.textInputLayout.EditText?.SetTextColor(this.Element.TextColor.ToAndroid());
}
private void ApplyEnabled()
{
this.textInputLayout.EditText.Enabled = this.Element.IsEnabled;
this.textInputLayout.EditText.SetCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null);
if (this.Element.IsEnabled)
{
this.ApplyIsInputValid();
this.SetHintLabelDefaultColor(this.Element.FloatingLabelColor.ToAndroid());
this.SetHintLabelActiveColor();
}
else
{
this.textInputLayout.EditText.SetPadding(0, 0, 0, 0);
this.textInputLayout.EditText.BackgroundTintList = ColorStateList.ValueOf(Color.Transparent);
this.SetHintLabelDefaultColor(Color.ParseColor("#9C9C9C"));
}
}
private void ApplyErrorText()
{
this.textInputLayout.ErrorEnabled = true;
this.textInputLayout.Error = this.Element.ErrorText;
}
private void ApplyIsInputValid()
{
if (!this.Element.IsInputValid.HasValue || this.Element.IsInputValid.Value)
{
this.textInputLayout.Error = null;
if (!this.Element.IsInputValid.HasValue || (!this.Element.IsMandatory && string.IsNullOrWhiteSpace(this.Element.Text)))
return;
this.SetIconFromKey("ce-check", "#04d1cd");
}
else
{
this.SetIconFromKey("ce-Cross_close", "#ff6161");
}
}
private void ApplyPlaceholder()
{
this.textInputLayout.HintEnabled = true;
this.textInputLayout.HintAnimationEnabled = true;
this.textInputLayout.Hint = this.Element.Placeholder;
}
private void ApplyIsWrapping()
{
if (this.Element.IsWrapping)
{
this.textInputLayout.EditText.InputType |= InputTypes.TextFlagCapSentences;
this.textInputLayout.EditText.SetHorizontallyScrolling(false);
this.textInputLayout.EditText.SetMaxLines(int.MaxValue);
}
else
{
this.textInputLayout.EditText.InputType &= ~InputTypes.TextFlagCapSentences;
this.textInputLayout.EditText.SetHorizontallyScrolling(true);
this.textInputLayout.EditText.SetMaxLines(1);
}
}
private void ApplyKeyboard()
{
this.textInputLayout.EditText.InputType = this.Element.Keyboard.ToInputType();
}
private void SetIconFromKey(string key, string color)
{
var icon = Iconize.FindIconForKey(key);
if (icon == null)
return;
var drawable = new IconDrawable(this.Context, icon).Color(Color.ParseColor(color)).SizeDp(17);
this.textInputLayout.EditText.SetCompoundDrawablesRelativeWithIntrinsicBounds(null, null, drawable, null);
}
But when property IsEnabled is binded, my floating label is gray and not blue (unless I have focus) whereas if IsEnabled = false Gray or IsEnabled = true Blue
, the floating label is in the correct color.
The only solution if found was to make element focus and unfocus immediatly after applying isEnabled if it was true.
For the life of me, I can't find a good solution. I don't know if it is me and I can't see it but I need help.
Thanks
I found a solution, in my ViewModel, by default , my binding IsEnabled will be true and I set it to false when required.
It works, thanks for your help.

Setting TextBlock Visibility on selection Change

Currently I have a combo box, and want to change the visibility of a TextBlock on certain selections.
Error it's producing Object reference not set to an instance of an object.
private void Selection(object sender, SelectionChangedEventArgs e)
{
if (Findpf() == 12)
{
DateAutoCompleteBox.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
DateAutoCompleteBox.Visibility = System.Windows.Visibility.Visible;
}
}
Tried = Visibility.Collapsed also and same result. How do i fix this ?
public uint Findpf()
{
if (Pf.Text == "Annual")
{
return 1;
}
if (Pf.Text == "Semi-annual")
{
return 2;
}
if (Pf.Text == "Tri-Annual")
{
return 3;
}
if (Pf.Text == "Quarterly")
{
return 4;
}
if (Pf.Text == "Bi-Monthly")
{
return 6;
}
if (Pf.Text == "Monthly")
{
return 12;
}
}
Initialization of Autocompletebox Xaml
<telerik:RadAutoCompleteBox x:Name="DateAutoCompleteBox" Visibility="Visible" Width="220"></telerik:RadAutoCompleteBox>
you can try this to check what value is null
private void Selection(object sender, SelectionChangedEventArgs e)
{
if(DateAutoCompleteBox == null)
{
MessageBox.Show("DateAutoCompleteBox is null"); return;
}
if(Pf == null)
{
MessageBox.Show("Pf is null"); return;
}
if(Pf.Text == null)
{
MessageBox.Show("Pf.Text is null"); return;
}
if (Findpf() == 12)
{
DateAutoCompleteBox.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
DateAutoCompleteBox.Visibility = System.Windows.Visibility.Visible;
}
}
DateAutoCompleteBox is set to null and you cant access or set properties on objects that isnt initialized.

Need a numeric only windows control TextBox [duplicate]

This question already has answers here:
How do I make a textbox that only accepts numbers?
(41 answers)
Closed 9 years ago.
I am creating an old-school dialog in c# using a System.Windows.Controls.TextBox .
Is there an easy way of limiting text input in this box to numeric only?
Thanks!
Just implement the onkeyup event handler and if the key pressed is not a Character.IsDigit then clear it.
http://msdn.microsoft.com/en-us/library/system.windows.controls.textbox.onkeyup(VS.95).aspx
You could consider using a MaskedTextBox, setting the Mask property accordingly.
This is an extract from my answer to an earlier question.
Add this class to your project
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class TextBoxFilter
{
[Flags()]
public enum Filters
{
None = 0,
Text = 1,
Numbers = 2,
AlphaNumeric = Filters.Text | Filters.Numbers,
Currency = 4,
All = Filters.Text | Filters.Numbers | Filters.Currency
}
Dictionary<TextBox, Filters> _keyFilter;
Dictionary<TextBox, string> _allowedKeys;
Dictionary<TextBox, string> _invalidKeys;
Dictionary<TextBox, Windows.Forms.KeyEventArgs> keyEventArgs;
private static string DecimalMark = Application.CurrentCulture.NumberFormat.NumberDecimalSeparator;
private static string NegativeMark = Application.CurrentCulture.NumberFormat.NegativeSign;
private static string CurrencySymb = Application.CurrentCulture.NumberFormat.CurrencySymbol;
private static string CurrencyDecimal = Application.CurrentCulture.NumberFormat.CurrencyDecimalSeparator;
public TextBoxFilter()
{
_keyFilter = new Dictionary<TextBox, Filters>();
_allowedKeys = new Dictionary<TextBox, string>();
_invalidKeys = new Dictionary<TextBox, string>();
keyEventArgs = new Dictionary<TextBox, KeyEventArgs>();
}
//set & remove filter
public void SetTextBoxFilter(TextBox textBox, Filters filter)
{
SetTextBoxFilter(textBox, filter, AllowedKeys(textBox), InvalidKeys(textBox));
}
public void SetTextBoxFilter(TextBox textBox, string allowedKeys)
{
SetTextBoxFilter(textBox, Strings.Filter(textBox), allowedKeys, InvalidKeys(textBox));
}
public void SetTextBoxFilter(TextBox textBox, string allowedKeys, string invalidKeys)
{
SetTextBoxFilter(textBox, Strings.Filter(textBox), allowedKeys, invalidKeys);
}
public void SetTextBoxFilter(TextBox textBox, Filters filter, string allowedKeys, string invalidKeys)
{
if (!_keyFilter.ContainsKey(textBox)) {
//add the textbox and its filter if it does not exist in
//the collection of registered textboxes
_keyFilter.Add(textBox, filter);
_allowedKeys.Add(textBox, allowedKeys);
_invalidKeys.Add(textBox, invalidKeys);
keyEventArgs.Add(textBox, new System.Windows.Forms.KeyEventArgs(Keys.None));
//add the event handlers
textBox.KeyDown += KeyDownUp;
textBox.KeyUp += KeyDownUp;
textBox.KeyPress += KeyPress;
textBox.Validating += Validating;
textBox.Disposed += Disposed;
} else {
//change the filter of the textbox if it exists in
//the collection of registered textboxes
_keyFilter(textBox) = filter;
_allowedKeys(textBox) = allowedKeys;
_invalidKeys(textBox) = invalidKeys;
}
}
public void RemoveTextBoxFilter(TextBox textBox)
{
if (_keyFilter.ContainsKey(textBox)) {
_keyFilter.Remove(textBox);
_allowedKeys.Remove(textBox);
_invalidKeys.Remove(textBox);
keyEventArgs.Remove(textBox);
textBox.KeyDown -= KeyDownUp;
textBox.KeyUp -= KeyDownUp;
textBox.KeyPress -= KeyPress;
textBox.Validating -= Validating;
textBox.Disposed -= Disposed;
}
}
public bool ContainsTextBox(TextBox textBox)
{
return _keyFilter.ContainsKey(textBox);
}
//properties
public Filters Filter {
get {
if (ContainsTextBox(textBox)) {
return _keyFilter.Item[textBox];
} else {
return Filters.None;
}
}
set { SetTextBoxFilter(textBox, value); }
}
public string AllowedKeys {
get {
if (ContainsTextBox(textBox)) {
return _allowedKeys(textBox);
} else {
return "";
}
}
set { SetTextBoxFilter(textBox, this.Filter(textBox), value, this.InvalidKeys(textBox)); }
}
public string InvalidKeys {
get {
if (ContainsTextBox(textBox)) {
return _invalidKeys(textBox);
} else {
return "";
}
}
set { SetTextBoxFilter(textBox, this.Filter(textBox), this.AllowedKeys(textBox), value); }
}
//event handlers
private void Disposed(object sender, System.EventArgs e)
{
RemoveTextBoxFilter((TextBox)sender);
}
private void KeyDownUp(object sender, System.Windows.Forms.KeyEventArgs e)
{
//assign the modifiers
keyEventArgs((TextBox)sender) = e;
}
private void KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
//ensure key pressed is in the allowed keys
object txt = (TextBox)sender;
object c = e.KeyChar;
bool allowKey = IsValidChar(txt, c, txt.SelectionStart);
//check for backspace & Ctrl combinations if the allowKey is still false
if (allowKey == false) {
if (keyEventArgs(txt).Control) {
//control modifier goes with A, X, C, V and Z for
//Select All, Cut, Copy, Paste and Undo respectively
object key = keyEventArgs(txt).KeyCode;
allowKey = (key == Keys.A || key == Keys.X || key == Keys.C || key == Keys.V || key == Keys.Z);
} else if (keyEventArgs(txt).KeyCode == Keys.Back) {
//allow the backspace key
allowKey = true;
}
}
//disable the key if it was not valid
if (!allowKey) {
e.Handled = true;
Interaction.Beep();
}
}
private void Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
object box = (TextBox)sender;
object boxFlags = _keyFilter(box);
//skip validation if the textbox allows all entries or there is no text
if (boxFlags == Filters.All | string.IsNullOrEmpty(box.Text))
return;
//otherwise check the characters entered
object txtChars = box.Text.ToCharArray;
bool isValidEntry = false;
//check each caracter for an invalid entry
for (i = 0; i <= txtChars.Length - 1; i++) {
object c = txtChars(i);
isValidEntry = IsValidChar(box, txtChars(i), i);
if (!isValidEntry) {
box.Select(i, 1);
break; // TODO: might not be correct. Was : Exit For
}
}
if (!isValidEntry)
e.Cancel = true;
if (!isValidEntry) {
Interaction.MsgBox("The text entered is invalid for the format " + boxFlags.ToString + "." + !string.IsNullOrEmpty(_allowedKeys(box)) ? Constants.vbCrLf + "Additional Allowed Keys: " + _allowedKeys(box) : "" + !string.IsNullOrEmpty(_invalidKeys(box)) ? Constants.vbCrLf + "Additional Invalid Keys: " + _invalidKeys(box) : "", MsgBoxStyle.Critical, "Invalid Entry");
}
}
private bool IsValidChar(TextBox textBox, char c, int charIndex)
{
//ensure key pressed is in the allowed keys
object pF = _keyFilter(textBox);
object aK = _allowedKeys(textBox);
object iK = _invalidKeys(textBox);
bool shouldAllow = false;
//if filter is set to all, return true unconditionally
if (pF == Filters.All)
return true;
//check preset filters
//check for text
if (EnumHasFlag(pF, Filters.Text)) {
if (!char.IsDigit(c)) {
shouldAllow = true;
} else {
//if the character is a digit, check for the number flag (AlphaNumerics)
if (EnumHasFlag(pF, Filters.Numbers)) {
shouldAllow = true;
}
}
}
//check for nubers
if (shouldAllow == false && EnumHasFlag(pF, Filters.Numbers)) {
if (char.IsDigit(c)) {
shouldAllow = true;
} else if (DecimalMark.Contains(c)) {
//allow the decimal if there is no decimal in the textbox's
//text or the selected text contains the mark
if (!textBox.Text.Substring(0, charIndex).Contains(c) || textBox.SelectedText.Contains(c)) {
shouldAllow = true;
}
} else if (NegativeMark.Contains(c) && (charIndex <= NegativeMark.IndexOf(c))) {
//allow the negative mark if we are at the start of the
//textbox
shouldAllow = true;
}
}
//check for currency
if (shouldAllow == false && EnumHasFlag(pF, Filters.Currency)) {
if (char.IsDigit(c)) {
shouldAllow = true;
} else if (CurrencyDecimal.Contains(c)) {
//allow the currency decimal mark if it does not exist in the
//textbox's text or the selected text contains the mark
if (!textBox.Text.Substring(0, charIndex).Contains(c) || textBox.SelectedText.Contains(c)) {
shouldAllow = true;
}
} else if (CurrencySymb.Contains(c) && (charIndex <= CurrencySymb.IndexOf(c))) {
//allow the currency symbol if we are in a valid position
shouldAllow = true;
}
}
//now check for extra allowed keys
if (!shouldAllow) {
shouldAllow = aK.Contains(c);
}
//and then check for extra invalid keys
if (shouldAllow && iK.Contains(c)) {
shouldAllow = false;
}
return shouldAllow;
}
[System.Diagnostics.DebuggerStepThrough()]
private bool EnumHasFlag(Enum value, Enum flag)
{
return (Convert.ToInt64(value) & Convert.ToInt64(flag)) == Convert.ToInt64(flag);
}
}
and then use it in your form as follows
public class Form1
{
TextBoxFilter filter = new TextBoxFilter();
private void Form1_Load(object sender, System.EventArgs e)
{
filter.SetTextBoxFilter(TextBox1, TextBoxFilter.Filters.Numbers);
}
public Form1()
{
Load += Form1_Load;
}
}
Also consider whether your design needs to restrict user input when typed/pasted or if it's OK to simply convert the user input to a number when he leaves the textbox.
Often the restrictive approach is harder to do (dealing with commas, periods, currency symbols, spaces, etc... can be a huge pain)
This method then becomes as simple as:
private void textBox1_Leave(object sender, EventArgs e)
{
textBox1.Text = VerifyNumeric(textBox1.Text);
}
private string VerifyNumeric(string text)
{
double value = 0;
double.TryParse(text, out value);
return value.ToString(); // could format here too.
}

Categories