Need a numeric only windows control TextBox [duplicate] - c#

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

Related

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).

How to create numeric Textbox Custom Control with dependency Property in WPF?

I want create a custom control for Numeric Text box with dependency property in WPF , in my solution , I add one WPF application and custom control (WPF) ,then in public class , I create dependency property ....
Now I don't know how can i write my rule for text box and which event is true?
Another question : What is my rule for numeric text box , that this text box must be give number and . and Separating .this custom Text box is for accounting system.
public static readonly DependencyProperty NumberTextbox;
static Numeric()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Numeric), new FrameworkPropertyMetadata(typeof(Numeric)));
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata("Enter Your Text", OnKeyDown);
NumberTextbox =DependencyProperty.Register("Text", typeof(TextBox), typeof(FrameworkElement), metadata);
}
public string NumberTXT
{
get { return (string)GetValue(NumberTextbox); }
set { SetValue(NumberTextbox, value); }
}
I recommend to you add another Dependency Property in example code below I named it Value
Also format your number by comma or NumberFormatInfo.CurrentInfo.NumberDecimalSeparator
and control caret location changes by two property SelectionLength and SelectionStart.
Finally for more detail and complete code WPF Maskable Number Entry TextBox
region Value property
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(NumericTextBox), new PropertyMetadata(new Double(), OnValueChanged));
private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
//var numericBoxControl = (NumericTextBox)sender;
}
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); Text = value.ToString("###,###,###"); }
}
endregion
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
base.OnPreviewTextInput(e);
var txt = e.Text.Replace(",", "");
e.Handled = !IsTextAllowed(txt);
if (IsTextAllowed(txt))
{
if (Text.Length == 3)
{
Text = Text.Insert(1,",");
SelectionLength = 1;
SelectionStart += Text.Length;
}
}
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (e.Key == Key.Back)
{
if (Text.Length == 5)
{
Text = Text.Replace(",", "");
SelectionLength = 1;
SelectionStart += Text.Length;
}
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
var txt = Text.Replace(",", "");
SetValue(ValueProperty, txt.Length==0?0:double.Parse(txt));
base.OnTextChanged(e);
}
private static bool IsTextAllowed(string text)
{
try
{
double.Parse(text);
return true;
}
catch (FormatException)
{
return false;
}
}
I don't understand your question exactly and why you need dependency proerties to make a numeric text box custom control. What you can do is to inherit from textbox and handle the PreviewTextInput, like it is solved in this question by Ray:
then you get:
public class NumericTextBox : TextBox
{
public NumericTextBox()
{
PreviewTextInput += NumericTextBox_PreviewTextInput;
}
void NumericTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !isTextAllowed(e.Text);
}
private static bool isTextAllowed(string text)
{
var regex = new Regex("[^0-9]+");
return !regex.IsMatch(text);
}
}
And you can use it like that:
<myNameSpace:NumericTextBox />
And now you can add any other validation you want.
I would also implement a solution for the pasting issue, something like (see also in the link):
private void textBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
var text = (String)e.DataObject.GetData(typeof(String));
if (!isTextAllowed(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
Good job, but let me do the following task with a UserControl in C #:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace NumericBox
{
public partial class NumericBox : TextBox
{
public NumericBox
{
this.TextAlign = HorizontalAlignment.Right;
this.Text = "0,00";
this.KeyPress += NumericBox_KeyPress;
}
public double NumericResult
{
get{
double d = Convert.ToDouble(this.Text.Replace('.', ','));
return d;
}
}
private void NumericBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && (e.KeyChar != '.'))
e.Handled = true;
if ((e.KeyChar == '.' && (sender as TextBox).Text.IndexOf('.') > -1))
e.Handled = true;
if (e.KeyChar == 13)
{
e.Handled = true;
SendKeys.Send("{TAB}");
}
}
}
}

Disable spacekey textbox in c# wpf

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

Setting value of a textfield to not go lower than zero

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

Best way to databind a group of radiobuttons in WinForms

I'm currently working on databinding some of my existing Windows Forms, and I've ran into an issue figuring out the proper way of databinding a group of radiobutton controls within a group box.
My business object has an integer property which I want to databind against 4 radiobuttons (where each of them represents the values 0 - 3).
I'm currently binding against a presenter object which works as the binder between the form and the business object, and the way I've done it now is to have 4 separate properties which each binds against each of these values (I do use INotifyPropertyChanged, but not including that here):
Private int _propValue;
Public bool PropIsValue0
{
get { return _propValue == 0; }
set
{
if (value)
_propValue = 0;
}
}
Public bool PropIsValue1 { // As above, but with value == 1 }
Public bool PropIsValue2 { // As above, but with value == 2 }
Public bool PropIsValue3 { // As above, but with value == 3 }
And I then bind each of the radiobuttons to their respective property as above.
This does not seem right to me, so any advice are highly appreciated.
Following is a generic RadioGroupBox implementation in the spirit of ArielBH's suggestion (some code borrowed from Jay Andrew Allen's RadioPanel). Just add RadioButtons to it, set their tags to different integers and bind to the 'Selected' property.
public class RadioGroupBox : GroupBox
{
public event EventHandler SelectedChanged = delegate { };
int _selected;
public int Selected
{
get
{
return _selected;
}
set
{
int val = 0;
var radioButton = this.Controls.OfType<RadioButton>()
.FirstOrDefault(radio =>
radio.Tag != null
&& int.TryParse(radio.Tag.ToString(), out val) && val == value);
if (radioButton != null)
{
radioButton.Checked = true;
_selected = val;
}
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
var radioButton = e.Control as RadioButton;
if (radioButton != null)
radioButton.CheckedChanged += radioButton_CheckedChanged;
}
void radioButton_CheckedChanged(object sender, EventArgs e)
{
var radio = (RadioButton)sender;
int val = 0;
if (radio.Checked && radio.Tag != null
&& int.TryParse(radio.Tag.ToString(), out val))
{
_selected = val;
SelectedChanged(this, new EventArgs());
}
}
}
Note that you can't bind to the 'Selected' property via the designer due to initialization order problems in InitializeComponent (the binding is performed before the radio buttons are initialized, so their tag is null in the first assignment). So just bind yourself like so:
public Form1()
{
InitializeComponent();
//Assuming selected1 and selected2 are defined as integer application settings
radioGroup1.DataBindings.Add("Selected", Properties.Settings.Default, "selected1");
radioGroup2.DataBindings.Add("Selected", Properties.Settings.Default, "selected2");
}
I know this post is old but in my search for an answer for this same problem I came across this post and it didn't solve my problem. I ended up having a lightbulb go off randomly just a minute ago and wanted to share my solution.
I have three radio buttons in a group box. I'm using a List<> of a custom class object as the data source.
Class Object:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BAL
{
class ProductItem
{
// Global Variable to store the value of which radio button should be checked
private int glbTaxStatus;
// Public variable to set initial value passed from
// database query and get value to save to database
public int TaxStatus
{
get { return glbTaxStatus; }
set { glbTaxStatus = value; }
}
// Get/Set for 1st Radio button
public bool Resale
{
// If the Global Variable = 1 return true, else return false
get
{
if (glbTaxStatus.Equals(1))
{
return true;
}
else
{
return false;
}
}
// If the value being passed in = 1 set the Global Variable = 1, else do nothing
set
{
if (value.Equals(true))
{
glbTaxStatus = 1;
}
}
}
// Get/Set for 2nd Radio button
public bool NeverTax
{
// If the Global Variable = 2 return true, else return false
get
{
if (glbTaxStatus.Equals(2))
{
return true;
}
else
{
return false;
}
}
// If the value being passed in = 2 set the Global Variable = 2, else do nothing
set
{
if (value.Equals(true))
{
glbTaxStatus = 2;
}
}
}
// Get/Set for 3rd Radio button
public bool AlwaysTax
{
// If the Global Variable = 3 return true, else return false
get
{
if (glbTaxStatus.Equals(3))
{
return true;
}
else
{
return false;
}
}
// If the value being passed in = 3 set the Global Variable = 3, else do nothing
set
{
if (value.Equals(true))
{
glbTaxStatus = 3;
}
}
}
// More code ...
Three seperate public variables with get/set accessing the same one global variable.
In the code behind, I have a function called during the Page_Load() setting all the controls databindings. For each radio button I add its own databinging.
radResale.DataBindings.Add("Checked", glbProductList, "Resale", true, DataSourceUpdateMode.OnPropertyChanged, false);
radNeverTax.DataBindings.Add("Checked", glbProductList, "NeverTax", true, DataSourceUpdateMode.OnPropertyChanged, false);
radAlwaysTax.DataBindings.Add("Checked", glbProductList, "Always", true, DataSourceUpdateMode.OnPropertyChanged, false);
I hope this helps someone!!
I think I would use my own GroupBox. I would bind the CustomGroupBox to your Model and set the correct RadioButton (using the tag or name properties) from the binded value.
This is my approach for binding a list of radio buttons to an enum.
Using the Enum as a string in the button's Tag property, I use the Binding.Format and Binding.Parse event to decide which button should be checked.
public enum OptionEnum
{
Option1 = 0,
Option2
}
OptionEnum _rbEnum = OptionEnum.Option1;
OptionEnum PropertyRBEnum
{
get { return _rbEnum; }
set
{
_rbEnum = value;
RaisePropertyChanged("PropertyRBEnum");
}
}
public static void FormatSelectedEnum<T>(object sender, ConvertEventArgs args) where T : struct
{
Binding binding = (sender as Binding);
if (binding == null) return;
Control button = binding.Control;
if (button == null || args.DesiredType != typeof(Boolean)) return;
T value = (T)args.Value;
T controlValue;
if (Enum.TryParse(button.Tag.ToString(), out controlValue))
{
args.Value = value.Equals(controlValue);
}
else
{
Exception ex = new Exception("String not found in Enum");
ex.Data.Add("Tag", button.Tag);
throw ex;
}
}
public static void ParseSelectedEnum<T>(object sender, ConvertEventArgs args) where T : struct
{
Binding binding = (sender as Binding);
if (binding == null) return;
Control button = binding.Control;
bool value = (bool)args.Value;
if (button == null || value != true) return;
T controlValue;
if (Enum.TryParse(button.Tag.ToString(), out controlValue))
{
args.Value = controlValue;
}
else
{
Exception ex = new Exception("String not found in Enum");
ex.Data.Add("Tag", button.Tag);
throw ex;
}
}
Then setup your data binding like this:
radioButton1.Tag = "Option1";
radioButton2.Tag = "Option2";
foreach (RadioButtonUx rb in new RadioButtonUx[] { radioButton1, radioButton2 })
{
Binding b = new Binding("Checked", this, "PropertyRBEnum");
b.Format += FormatSelectedRadioButton<OptionEnum>;
b.Parse += ParseSelectedRadioButton<OptionEnum>;
rb.DataBindings.Add(b);
}
I started to resolve the same problematic.
I used a RadioButtonBinding class which encapsulates all radiobuttons about an enum in the data source.
This following class keeps all radio buttons in a list and makes a lookup for the enum :
class RadioButtonBinding : ILookup<System.Enum, System.Windows.Forms.RadioButton>
{
private Type enumType;
private List<System.Windows.Forms.RadioButton> radioButtons;
private System.Windows.Forms.BindingSource bindingSource;
private string propertyName;
public RadioButtonBinding(Type myEnum, System.Windows.Forms.BindingSource bs, string propertyName)
{
this.enumType = myEnum;
this.radioButtons = new List<System.Windows.Forms.RadioButton>();
foreach (string name in System.Enum.GetNames(this.enumType))
{
System.Windows.Forms.RadioButton rb = new System.Windows.Forms.RadioButton();
rb.Text = name;
this.radioButtons.Add(rb);
rb.CheckedChanged += new EventHandler(rb_CheckedChanged);
}
this.bindingSource = bs;
this.propertyName = propertyName;
this.bindingSource.DataSourceChanged += new EventHandler(bindingSource_DataSourceChanged);
}
void bindingSource_DataSourceChanged(object sender, EventArgs e)
{
object obj = this.bindingSource.Current;
System.Enum item = obj.GetType().GetProperty(propertyName).GetValue(obj, new object[] { }) as System.Enum;
foreach (System.Enum value in System.Enum.GetValues(this.enumType))
{
if (this.Contains(value))
{
System.Windows.Forms.RadioButton rb = this[value].First();
if (value.Equals(item))
{
rb.Checked = true;
}
else
{
rb.Checked = false;
}
}
}
}
void rb_CheckedChanged(object sender, EventArgs e)
{
System.Windows.Forms.RadioButton rb = sender as System.Windows.Forms.RadioButton;
System.Enum val = null;
try
{
val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
}
catch(Exception ex)
{
// cannot occurred if code is safe
System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
}
object obj = this.bindingSource.Current;
obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] { });
this.bindingSource.CurrencyManager.Refresh();
}
public int Count
{
get
{
return System.Enum.GetNames(this.enumType).Count();
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.radioButtons.GetEnumerator();
}
public bool Contains(Enum key)
{
return System.Enum.GetNames(this.enumType).Contains(key.ToString());
}
public IEnumerable<System.Windows.Forms.RadioButton> this[Enum key]
{
get
{
return this.radioButtons.FindAll(a => { return a.Text == key.ToString(); });
}
}
IEnumerator<IGrouping<Enum, System.Windows.Forms.RadioButton>> IEnumerable<IGrouping<Enum, System.Windows.Forms.RadioButton>>.GetEnumerator()
{
throw new NotImplementedException();
}
public void AddControlsIntoGroupBox(System.Windows.Forms.GroupBox gb)
{
System.Windows.Forms.FlowLayoutPanel panel = new System.Windows.Forms.FlowLayoutPanel();
panel.Dock = System.Windows.Forms.DockStyle.Fill;
panel.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
foreach (System.Windows.Forms.RadioButton rb in this.radioButtons)
{
panel.Controls.Add(rb);
}
gb.Controls.Add(panel);
}
}
You are using the class into a form by adding that code in the constructor of the form:
public PageView()
{
InitializeComponent();
RadioButtonBinding rbWidth = new RadioButtonBinding(typeof(Library.EnumConstraint), this.pageBindingSource, "ConstraintWidth");
rbWidth.AddControlsIntoGroupBox(this.groupBox1);
RadioButtonBinding rbHeight = new RadioButtonBinding(typeof(Library.EnumConstraint), this.pageBindingSource, "ConstraintHeight");
rbHeight.AddControlsIntoGroupBox(this.groupBox3);
this.pageBindingSource.CurrentItemChanged += new EventHandler(pageBindingSource_CurrentItemChanged);
}
Set the tag name of your radio buttons to something that represents the value.
Create a string setting, for example, OptionDuplicateFiles, and give it the default value of the tag name for your default radio button.
To save your checked radio button:
Settings.Default.OptionDuplicateFiles = gbxDuplicateFiles.Controls
.OfType<RadioButton>()
.Where(b => b.Checked)
.Select(b => b.Tag)
.First()
.ToString();
To load your checked radio button:
(gbxDuplicateFiles.Controls
.OfType<RadioButton>()
.Where(b => b.Tag.ToString() == Settings.Default.OptionDuplicateFiles)
.First())
.Checked = true;
Tada!
I liked the idea of a RadioButtonGroupBox but I decided to create a version that is self supporting.
There is no reason to add value to Tag attribute or to introduce new value attributes.
Any assigned radio button is still a member of the RadioButtonGroupBox and the sequence of radiobuttons is defined during development.
Soo, I modified the code.
Now I can get and set the selected radiobutton by index position, By Control Name and by Text.
BTW Text is only useable if your asssigned Text is different for each radiobutton.
public class RadioButtonGroupBox : GroupBox
{
public event EventHandler SelectedChanged = delegate { };
int _nIndexPosCheckRadioButton = -1;
int _selected;
public int Selected
{
get
{
return _selected;
}
}
public int CheckedRadioButtonIndexPos
{
set
{
int nPosInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Set the RB that should be checked
if (nPosInList == value)
{
item.Checked = true;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosInList;
}
get
{
int nPosInList = -1;
int nPosCheckeItemInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Find the RB that is checked
if (item.Checked)
{
nPosCheckeItemInList = nPosInList;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosCheckeItemInList;
return _nIndexPosCheckRadioButton;
}
}
public string CheckedRadioButtonByText
{
set
{
int nPosInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Set the RB that should be checked
if (item.Text == value)
{
item.Checked = true;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosInList;
}
get
{
string cByTextValue = "__UNDEFINED__";
int nPosInList = -1;
int nPosCheckeItemInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Find the RB that is checked
if (item.Checked)
{
cByTextValue = item.Text;
nPosCheckeItemInList = nPosInList;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosCheckeItemInList;
return cByTextValue;
}
}
public string CheckedRadioButtonByName
{
set
{
int nPosInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Set the RB that should be checked
if (item.Name == value)
{
item.Checked = true;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosInList;
}
get
{
String cByNameValue = "__UNDEFINED__";
int nPosInList = -1;
int nPosCheckeItemInList = -1;
foreach (RadioButton item in this.Controls.OfType<RadioButton>())
{
// There are RadioButtonItems in the list...
nPosInList++;
// Find the RB that is checked
if (item.Checked)
{
cByNameValue = item.Name;
nPosCheckeItemInList = nPosInList;
// We can stop with the loop
break;
}
}
_nIndexPosCheckRadioButton = nPosCheckeItemInList;
return cByNameValue;
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
var radioButton = e.Control as RadioButton;
if (radioButton != null)
radioButton.CheckedChanged += radioButton_CheckedChanged;
}
void radioButton_CheckedChanged(object sender, EventArgs e)
{
_selected = CheckedRadioButtonIndexPos;
SelectedChanged(this, new EventArgs());
}
}
My approach is to put each radio button into its own panel before binding them to a boolean property:
public static Binding Bind<TObject>(this RadioButton control, object dataSource, string dataMember)
{
// Put the radio button into its own panel
Panel panel = new Panel();
control.Parent.Controls.Add(panel);
panel.Location = control.Location;
panel.Size = control.Size;
panel.Controls.Add(control);
control.Location = new Point(0, 0);
// Do the actual data binding
return control.DataBindings.Add("Checked", dataSource, dataMember);
}
I would like to make an observation about the code block that might be helpful to people reading these posts. The following code may not always work as expected due to it's structure.
try
{
val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
}
catch(Exception ex)
{
// cannot occurred if code is safe
System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
}
object obj = this.bindingSource.Current;
obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] {
}
);
this.bindingSource.CurrencyManager.Refresh();
If an error occurs in the try block, the catch block will be executed. The code will continue to execute after the catch block. Since there was no handling of binding source, the variables following the catch could end up in a indeterminate state and may throw another exception that may or may not be handled.
A better approach is as follows
try
{
val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
object obj = this.bindingSource.Current;
obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] { });
this.bindingSource.CurrencyManager.Refresh();
}
catch(EntityException ex)
{
// handle error
}
catch(Exception ex)
{
// cannot occurred if code is safe
System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
}
This allows the enum value error to be handled as well as other errors that may occur. However use the EntityException or variations of it before the Exception block (all decendents of Exception have to come first). One can get specific entity state information for a entity framework error by using the entity framework classes instead of the Exception base class. This can be helpful for debugging or providing clearer run time messages for the user.
When I setup try-catch blocks I like to view it as a "layer" on top of the code. I make decisions about the flow of the exceptions through out the program, their display to the user, and what ever cleanup is required to allow the program to continue working properly without objects in a indeterminate state that can cascade to other errors.
I liked Jan Hoogma GroupBox binding but preferred more control over values you could bind to started by creating RadioButtonBIndable which can have values of different types assigned to it which are strongly typed.
public class RadioButtonBindable : RadioButton
{
public Int32 ValueInt { get; set; }
public Decimal ValueDecimal { get; set; }
public String ValueString { get; set; }
public Boolean ValueBoolean { get; set; }
}
Then I can work on the GroupBox and create GroupBoxBindable that can be bound to any of the values in the RadioButtonBindable (you could bind to multiple values e.g. ValueInt = 23 and ValueString = "Example Text"
You can also set a default value in case of teh unlikeley event where there is no RadioButton selected.
public class RadioButtonGroupBoxBindable : GroupBox, INotifyPropertyChanged
{
//public event EventHandler SelectedChanged = delegate { };
public event EventHandler ValueIntChanged = delegate { };
public event EventHandler ValueDecimalChanged = delegate { };
public event EventHandler ValueStringChanged = delegate { };
public event EventHandler ValueBooleanChanged = delegate { };
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public Int32 DefaultValueInt { get; set; }
public Decimal DefaultValueDecimal { get; set; }
public String DefaultValueString { get; set; }
public Boolean DefaultValueBoolean { get; set; }
public Boolean ValueBoolean
{
set
{
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.ValueBoolean == value)
{
item.Checked = true;
break;
}
}
}
}
get
{
Boolean retVal = DefaultValueBoolean;
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.Checked)
{
retVal = item.ValueBoolean;
break;
}
}
}
return retVal;
}
}
public int ValueInt
{
set
{
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.ValueInt == value)
{
item.Checked = true;
break;
}
}
}
}
get
{
int retVal = DefaultValueInt;
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.Checked)
{
retVal = item.ValueInt;
break;
}
}
}
return retVal;
}
}
public decimal ValueDecimal
{
set
{
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.ValueDecimal == value)
{
item.Checked = true;
break;
}
}
}
}
get
{
decimal retVal = DefaultValueDecimal;
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.Checked)
{
retVal = item.ValueDecimal;
break;
}
}
}
return retVal;
}
}
public string ValueString
{
set
{
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.ValueString == value)
{
item.Checked = true;
break;
}
}
}
}
get
{
string retVal = DefaultValueString;
foreach (Control control in this.Controls)
{
if (control is RadioButtonBindable)
{
RadioButtonBindable item = (RadioButtonBindable)control;
if (item.Checked)
{
retVal = item.ValueString;
break;
}
}
}
return retVal;
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
var radioButton = e.Control as RadioButtonBindable;
if (radioButton != null)
radioButton.CheckedChanged += radioButton_CheckedChanged;
}
void radioButton_CheckedChanged(object sender, EventArgs e)
{
if (((RadioButtonBindable)sender).Checked)
{
OnPropertyChanged("ValueInt");
OnPropertyChanged("ValueDecimal");
OnPropertyChanged("ValueString");
OnPropertyChanged("ValueBoolean");
ValueIntChanged(this, new EventArgs());
ValueDecimalChanged(this, new EventArgs());
ValueStringChanged(this, new EventArgs());
ValueBooleanChanged(this, new EventArgs());
}
}
}

Categories