Setting value of a textfield to not go lower than zero - c#

How do i set a textfield so it cant go lower than 0?

If you are using Windows Forms you can simply use the NumericUpDown, it also exposes Minimum and Maximum value fields.
If for some reason you will need to roll your own, you will probably need to attach yourself to the event which is fired when the text is changed. If the content of the text field is numeric and is lower than 0, then simply override the text with 0 or whatever value you wish to provide.

win form code :-
private void btnArrowUp_Click(object sender, EventArgs e)
{
int val = 0;
if (Convert.ToInt32(textBoxValue.Text) <= 1000) //convert the textBox value to integer and check for your Upper Limit (here i have set it to 1000)
{
// if so, increment by your step (i used 1)
val = Convert.ToInt32(textBoxValue.Text);
val += 1;
textBoxValue.Text = val.ToString();
labelErrorMessage.Visible = false;
}
else
{
// otherwise, give a message and reset to your Default (i used 0)
textBoxValue.Text = "0";
labelErrorMessage.Text = "1000 (Zero) is the Max limit !";
labelErrorMessage.Visible = true;
}
}
private void btnArrowDown_Click(object sender, EventArgs e)
{
int val = 0;
if (Convert.ToInt32(textBoxValue.Text) > 0) //convert the textBox value to integer and check for your Lower Limit (here i have set it to 0)
{
// if so, decrement by your step (i used 1)
val = Convert.ToInt32(textBoxValue.Text);
val -= 1;
textBoxValue.Text = val.ToString();
labelErrorMessage.Visible = false;
}
else
{
// otherwise, give a message and reset to your Default (i used 0)
textBoxValue.Text = "0";
labelErrorMessage.Text = "0 (Zero) is the lowest limit !";
labelErrorMessage.Visible = true;
}
}

Use NumericUpDown instead of text field and set Minimum = 0 or follow this
Add Balloon.cs
public enum TooltipIcon
{
None,
Info,
Warning,
Error
}
public class Balloon
{
private Control m_parent;
private string m_text = "FMS Balloon Tooltip Control Display Message";
private string m_title = "FMS Balloon Tooltip Message";
private TooltipIcon m_titleIcon = TooltipIcon.None;
private const int ECM_FIRST = 0x1500;
private const int EM_SHOWBALLOONTIP = ECM_FIRST + 3;
[DllImport("User32", SetLastError = true)]
private static extern int SendMessage(
IntPtr hWnd,
int Msg,
int wParam,
IntPtr lParam);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct EDITBALLOONTIP
{
public int cbStruct;
public string pszTitle;
public string pszText;
public int ttiIcon;
}
public Balloon()
{
}
public Balloon(Control parent)
{
m_parent = parent;
}
public void Show()
{
EDITBALLOONTIP ebt = new EDITBALLOONTIP();
ebt.cbStruct = Marshal.SizeOf(ebt);
ebt.pszText = m_text;
ebt.pszTitle = m_title;
ebt.ttiIcon = (int)m_titleIcon;
IntPtr ptrStruct = Marshal.AllocHGlobal(Marshal.SizeOf(ebt));
Marshal.StructureToPtr(ebt, ptrStruct, false);
System.Diagnostics.Debug.Assert(m_parent != null, "Parent control is null", "Set parent before calling Show");
int ret = SendMessage(m_parent.Handle, EM_SHOWBALLOONTIP, 0, ptrStruct);
Marshal.FreeHGlobal(ptrStruct);
}
public void Show(string text, Control parent, string title, TooltipIcon icon)
{
}
public string Title
{
get
{
return m_title;
}
set
{
m_title = value;
}
}
public TooltipIcon TitleIcon
{
get
{
return m_titleIcon;
}
set
{
m_titleIcon = value;
}
}
public string Text
{
get
{
return m_text;
}
set
{
m_text = value;
}
}
public Control Parent
{
get
{
return m_parent;
}
set
{
m_parent = value;
}
}
}
Add the following code to your Form
private readonly Balloon _balloonTip = new Balloon();
public Form1()
{
InitializeComponent();
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = HandledNumerics(sender, e);
}
private bool HandledNumerics(object sender, KeyPressEventArgs e)
{
var handled = !(Char.IsDigit(e.KeyChar) || (e.KeyChar == (char)Keys.Back) || (e.KeyChar == (char)Keys.Delete));
if (handled)
{
ShowBalloon("Warning", "Invalid Input!\nOnly numeric value is acceptable", (TextBox)sender);
}
return handled;
}
private void ShowBalloon(string title, string text, Control parent, TooltipIcon icon = TooltipIcon.Warning)
{
_balloonTip.Title = title;
_balloonTip.Text = text;
_balloonTip.Parent = parent;
_balloonTip.TitleIcon = icon;
_balloonTip.Show();
}
And get the result just like

simple way, just create a funcion that verifies is the value is bellow 0 and call it when the text changes, or in your down_click function if the text field is locked to keybord input.
private void bellowZero()
{
if (Convert.ToInt32(textBox1.Text) < 0)
{
textBox1.Text = "0";
}
}

Related

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;
}

UWP goBack to cached page closes app

I have a RootPage as root view. In my RootPage I've added this to support the back button:
SystemNavigationManager.GetForCurrentView().BackRequested += SystemNavigationManager_BackRequested;
private void SystemNavigationManager_BackRequested(object sender, BackRequestedEventArgs e) {
bool handled = e.Handled;
if (this.AppFrame == null)
return;
if (this.AppFrame.CanGoBack && !handled) {
// If not, set the event to handled and go back to the previous page in the app.
handled = true;
this.AppFrame.GoBack();
}
e.Handled = handled;
}
This works as expected (the user goes to the previous page if there is one). But when I enable caching for one page it doesn't work anymore:
this.NavigationCacheMode = NavigationCacheMode.Enabled;
I've also tried Required but that gives the same result.
When I press the back button now it just closes the app instead of going back. The code for going back is executed though, so I don't know what I'm doing wrong.
Update
I've located the exception. I have a custom TextBox which fills the Text with a currency symbol and a space. This is essentially what creates the exception:
this.Loaded += (sender, args) => {
Text = "$ ";
};
Note: it is not the currency symbol that breaks the app, but the fact that I set the Text in Loaded when returning to this Page (page cache should handle this now, so that's conflicting I guess)
Update 2
Code sample to reproduce problem. Custom TextBox:
public class RegexTextBox : TextBox {
private string regex = "";
private string previousText = "";
private int previousSelectionStart = 0;
public RegexTextBox() {
this.Loaded += (sender, args) => {
switch(RegexType) {
case RegexTextBoxTypes.Number:
regex = "^([0-9]+|)$";
break;
case RegexTextBoxTypes.Currency:
regex = "^€ ([0-9]+|)$";
Text = "€ ";
break;
}
this.TextChanging += input_TextChanging;
this.SelectionChanged += input_SelectionChanged;
previousText = Text;
previousSelectionStart = SelectionStart;
};
}
private void input_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args) {
if (regex.Length > 0) {
if (!Regex.IsMatch(Text, regex)) {
Text = previousText;
SelectionStart = previousSelectionStart;
}
previousText = Text;
}
}
private void input_SelectionChanged(object sender, RoutedEventArgs e) {
previousSelectionStart = SelectionStart;
}
public bool isEmpty() {
if(RegexType == RegexTextBoxTypes.Currency) {
return !Regex.IsMatch(Text, "^€ ([0-9]+)$");
}
return Text.Length == 0;
}
public void ClearText() {
if(RegexType == RegexTextBoxTypes.Currency) {
Text = "€ ";
} else {
Text = "";
}
}
public string GetPlainText() {
if (RegexType == RegexTextBoxTypes.Currency) {
return Text.Substring(2, Text.Length - 2);
}
return Text;
}
public RegexTextBoxTypes RegexType {
get { return (RegexTextBoxTypes)GetValue(RegexTypeProperty); }
set { SetValue(RegexTypeProperty, value); }
}
public static readonly DependencyProperty RegexTypeProperty =
DependencyProperty.Register("RegexType", typeof(RegexTextBoxTypes), typeof(RegexTextBox), new PropertyMetadata(RegexTextBoxTypes.Normal));
}
public enum RegexTextBoxTypes {
Normal,
Number,
Currency
}
In your xaml put this in your grid (controls namespace points to the location of the custom TextBox):
<controls:RegexTextBox
InputScope="Number"
RegexType="Currency"/>
Then in the constructor of the page put this:
this.NavigationCacheMode = NavigationCacheMode.Required;
Make sure the custom TextBox is filled with some numbers. Then navigate to a new page and back, when navigating back it should close the app due to an uncaught exception (though visual studio won't show there has been an exception).

Overrided Text property of a Textbox does not refresh properly

I'm creating a custom control (a watermarked textbox) and it inherits from Textbox. As of now, the textbox correctly shows the watermark on losing focus when there's no text and deletes it when the textbox gets focus (it even changes the text's color if it's the watermark). What I want it to do is to report that it has no text when it's showing the watermark, so I'm trying to override the Text property.
Code as follows:
public class WatermarkedTextbox : TextBox
{
private bool _isWatermarked;
private string _watermark;
public string Watermark
{
get { return _watermark; }
set { _watermark = value; }
}
[Bindable(false), EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get
{
return _isWatermarked ? string.Empty : base.Text;
}
set
{
base.Text = value;
}
}
public WatermarkedTextbox()
{
GotFocus += WatermarkedTextbox_GotFocus;
LostFocus += WatermarkedTextbox_LostFocus;
}
private void WatermarkedTextbox_LostFocus(object sender, EventArgs e)
{
if (Text.Length == 0)
{
ForeColor = SystemColors.InactiveCaption;
Text = _watermark;
_isWatermarked = true;
}
}
private void WatermarkedTextbox_GotFocus(object sender, EventArgs e)
{
if (_isWatermarked)
{
ForeColor = SystemColors.ControlText;
Text = string.Empty;
_isWatermarked = false;
}
}
}
Problem is, when the textbox gets focus it does not delete the watermark.
What am I missing/doing wrong here?
Ah sorry I didn't read that clearly. Alternatively you might want to notify not by overriding the Text Property.
You can make use of event as such:
public class WatermarkedTextbox : TextBox, INotifyPropertyChanged
{
private bool _isWatermarked;
private string _watermark;
public string Watermark
{
get { return _watermark; }
set { _watermark = value; }
}
public bool IsWaterMarked
{
get
{
return _isWatermarked;
}
set
{
_isWatermarked = value;
OnPropertyChanged("IsWaterMarked");
}
}
public WatermarkedTextbox()
{
GotFocus += WatermarkedTextbox_GotFocus;
LostFocus += WatermarkedTextbox_LostFocus;
}
private void WatermarkedTextbox_LostFocus(object sender, EventArgs e)
{
if (Text.Length == 0)
{
ForeColor = SystemColors.InactiveCaption;
Text = _watermark;
IsWaterMarked = true;
}
}
private void WatermarkedTextbox_GotFocus(object sender, EventArgs e)
{
if (_isWatermarked)
{
ForeColor = SystemColors.ControlText;
Text = string.Empty;
IsWaterMarked = false;
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then on the Main Form, you can subscribe and add a handler to the PropertyChanged event:
//somewhere, like in the forms constructor, you need to subscribe to this event
watermarkedTextbox2.PropertyChanged += watermarkedTextbox2_PropertyChanged;
// the handler function
void watermarkedTextbox2_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsWaterMarked")
{
if (watermarkedTextbox2.IsWaterMarked)
; //handle here
else
; //handle here
}
}
Windows supports watermarking for text boxes (and other edit controls such as combo boxes), they call it the "cue banner". Note however that this does not work for multi-line text boxes.
Setting the cue banner on a supported control is simply a matter of using the Win32 API to send an EM_SETCUEBANNER message to the control containing the watermark text. Windows will then handle detecting when the control is empty or has focus and do all the hard work for you, you won't need to use events to manage state. The cue banner is also ignored when you get the control's Text property.
I use the following helper class to set the cue banner (works for combo boxes too):
public class CueBannerHelper
{
#region Win32 API's
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
public int cbSize;
public RECT rcItem;
public RECT rcButton;
public IntPtr stateButton;
public IntPtr hwndCombo;
public IntPtr hwndItem;
public IntPtr hwndList;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
/// <summary>Used to get the current Cue Banner on an edit control.</summary>
public const int EM_GETCUEBANNER = 0x1502;
/// <summary>Used to set a Cue Banner on an edit control.</summary>
public const int EM_SETCUEBANNER = 0x1501;
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hwnd, ref COMBOBOXINFO pcbi);
[DllImport("user32.dll")]
public static extern Int32 SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
#endregion
#region Method members
public static void SetCueBanner(Control control, string cueBanner) {
if (control is ComboBox) {
CueBannerHelper.COMBOBOXINFO info = new CueBannerHelper.COMBOBOXINFO();
info.cbSize = Marshal.SizeOf(info);
CueBannerHelper.GetComboBoxInfo(control.Handle, ref info);
CueBannerHelper.SendMessage(info.hwndItem, CueBannerHelper.EM_SETCUEBANNER, 0, cueBanner);
}
else {
CueBannerHelper.SendMessage(control.Handle, CueBannerHelper.EM_SETCUEBANNER, 0, cueBanner);
}
}
#endregion
}
All that is then required to implement the watermark on the custom TextBox control is the following property (the attributes at the top are for the control's design-time properties) :
/// <summary>
/// Gets or sets the watermark that the control contains.
/// </summary>
[Description("The watermark that the control contains."),
Category("Appearance"),
DefaultValue(null),
Browsable(true)
]
public string Watermark {
get { return this._watermark; }
set {
this._watermark = value;
CueBannerHelper.SetCueBanner(this, value);
}
}
Delete your overriden Text property, then it will work!
Delete these lines:
[Bindable(false), EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get
{
return _isWatermarked ? string.Empty : base.Text;
}
set
{
base.Text = value;
}
}
Hans Passant's comment was the correct answer to my question. Also, thanks to everyone that took time to offer help.
I finally decided to go the simplest route (handling PropertyChanged seems too convoluted for this particular need and hooking Windows APIs leaves out multiline textboxes so it's not an option).
In case someone needs it, here's the code:
public class WatermarkedTextbox : TextBox
{
private bool _isWatermarked;
private string _watermark;
public string Watermark
{
get { return _watermark; }
set { _watermark = value; }
}
[Bindable(false), EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get
{
return _isWatermarked ? string.Empty : base.Text;
}
set
{
base.Text = value;
}
}
public WatermarkedTextbox()
{
GotFocus += WatermarkedTextbox_GotFocus;
LostFocus += WatermarkedTextbox_LostFocus;
}
private void WatermarkedTextbox_LostFocus(object sender, EventArgs e)
{
if (Text.Length == 0)
{
_isWatermarked = true;
ForeColor = SystemColors.InactiveCaption;
Text = _watermark;
}
}
private void WatermarkedTextbox_GotFocus(object sender, EventArgs e)
{
if (_isWatermarked)
{
_isWatermarked = false;
ForeColor = SystemColors.ControlText;
Text = string.Empty;
}
}
}

Implementing a PasswordChar property to my custom C# Windows Form TextBox class that supports placeholders

I'm trying to implement a PasswordChar property to my TextBox class that cooperates with my Placeholder class. This is my code:
class MyTextBox : TextBox
{
public Placeholder Placeholder;
public MyTextBox()
{
Placeholder = new Placeholder(this);
GotFocus += new EventHandler(_GotFocus);
LostFocus += new EventHandler(_LostFocus);
}
private void _GotFocus(object sender, EventArgs e)
{
Placeholder.Active = false;
}
private void _LostFocus(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(Text))
Placeholder.Active = true;
}
}
class Placeholder
{
private MyTextBox textBox;
private bool active = false;
public bool Active
{
get { return active; }
set
{
if (value)
{
textBox.ForeColor = ForeColor;
textBox.Text = Text;
}
else if (active && !value)
{
textBox.ForeColor = previousForeColor;
textBox.Text = string.Empty;
}
active = value;
}
}
private Color previousForeColor;
private Color foreColor = Color.Gray;
public Color ForeColor
{
get { return foreColor; }
set
{
previousForeColor = ForeColor;
ForeColor = value;
}
}
private string text = "Placeholder";
public string Text
{
get { return text; }
set
{
text = value;
if (active)
textBox.Text = Text;
}
}
public Placeholder(MyTextBox textBox)
{
this.textBox = textBox;
}
}
The way I want it to work is if I set the TextBox's property PasswordChar to anything but '\0' then it should be set to '\0' when the TextBox's property's Placeholder's property Active is true, and should be set back to whatever the PasswordChar was when it's set to false.
In other words, if PasswordChar is set then unmask the text when the Active property is true, and remask the text when it's set back to false.
It sounds as simple as storing the original PasswordChar in a temporary variable:
class Placeholder
{
private MyTextBox textBox;
private bool active = false;
private char? originalPasswordChar = null;
public bool Active
{
get { return active; }
set
{
if (value)
{
textBox.ForeColor = ForeColor;
textBox.Text = Text;
if (originalPasswordChar == null) originalPasswordChar = textBox.PasswordChar;
}
else if (active && !value)
{
textBox.ForeColor = previousForeColor;
textBox.Text = string.Empty;
textBox.PasswordChar = originalPasswordChar.Value;
originalPasswordChar = null;
}
active = value;
}
}
// (...)
}

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