I haven't used hardly any of the new features of 2.0 yet, so I decided to look at the new MaskedTextBox. Wow ... what a useless piece of garbage ... at least for decimal data, I didn't try using it for anything else just yet. There may be tricks one has to know to get it working correctly, but I haven't researched it at all.
We created our own NumericTextBox sub-class in our 1.1 app (we've ported the app to 2.0, but haven't taken advantage of any new features as of yet). Here's what we've got:
This has been subclassed from our own TextBox subclass, but I think it has everything in it that does the numeric checking and our TextBox subclass is not needed. I don't know for sure though, so test this out just sub-classing from the .NET textbox and if it still doesn't do it correctly, let me know and I can post the code for our TextBox class.
public class MyNumericTextBox : System.Windows.Forms.TextBox
{
#region Declarations
private int m_DecimalPlaces = 0;
private decimal m_Maximum = 100;
private decimal m_Minimum = 0;
protected char[] AllowedChars = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-'};
protected string m_FormatString = "##0";
#endregion
#region Constructor
public MyNumericTextBox ()
{
this.MaxLength = 3;
this.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
}
#endregion
#region Methods
public void SetText(object val)
{
if (this.m_Minimum > 0)
{
if (val is Decimal)
this.Text = ((decimal)val).ToString(this.m_FormatString);
}
else
{
if (val is int)
this.Text = ((int)val).ToString(this.m_FormatString);
}
}
#endregion
#region Events
protected override void FormatHandler(object sender, ConvertEventArgs e)
{
if (e.Value == System.DBNull.Value)
e.Value = (int)0;
if (e.Value is Decimal && e.DesiredType == typeof(string))
e.Value = ((decimal)e.Value).ToString(this.m_FormatString);
else if (e.Value is int && e.DesiredType == typeof(string))
e.Value = ((int)e.Value).ToString(this.m_FormatString);
}
protected override void ParseHandler(object sender, ConvertEventArgs e)
{
if (((string)e.Value).Trim().Length == 0)
e.Value = "0";
if (e.DesiredType == typeof(decimal))
e.Value = Decimal.Parse((string)e.Value, System.Globalization.NumberStyles.Any);
else if (e.DesiredType == typeof(int))
{
string[] s = ((string)e.Value).Trim().Split('.');
e.Value = Decimal.Parse(s[0], System.Globalization.NumberStyles.Any);
}
}
protected override void KeyPressHandler(object sender, KeyPressEventArgs e)
{
bool allowed = false;
for (int i = 0; i < this.AllowedChars.Length; i++)
{
if (e.KeyChar == this.AllowedChars[i])
{
allowed = true;
break;
}
}
if (allowed == false)
e.Handled = true;
else if ((e.KeyChar == '.' || e.KeyChar == '-') && this.Text.IndexOf(e.KeyChar) >= 0)
e.Handled = true;
if (this.m_DecimalPlaces > 0)
{
}
}
protected override void ValidatingHandler(object sender, CancelEventArgs e)
{
if (this.Text.Trim().Length == 0)
return;
try
{
decimal test = Decimal.Parse(this.Text);
if (test > this.m_Maximum || test < this.m_Minimum)
{
string message = "The value for this field must be between " +
this.m_Maximum.ToString() + " and " + this.m_Minimum.ToString() + "!";
string caption = "Value out of range";
MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Cancel = true;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
#endregion
#region Properties
[Description("The number of digits to display after the decimal.")]
[DefaultValue(0)]
public int DecimalPlaces
{
get {return this.m_DecimalPlaces;}
set
{
this.m_DecimalPlaces = value;
if (value > 0 && this.m_Minimum < 0)
this.AllowedChars = new char[] {(char)Keys.Back, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.', '-'};
else if (value > 0)
this.AllowedChars = new char[] {(char)Keys.Back, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.'};
else if (this.m_Minimum < 0)
this.AllowedChars = new char[] {(char)Keys.Back, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-'};
else
this.AllowedChars = new char[] {(char)Keys.Back, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
string[] format = this.m_FormatString.Split(new char[] {'.'}, 2);
if (value > 0)
{
string s = new string('0', value);
this.m_FormatString = format[0] + "." + s;
}
else
this.m_FormatString = format[0];
this.MaxLength = this.m_FormatString.Length;
if (this.m_Minimum < 0 && this.m_Minimum.ToString().Length > this.m_Maximum.ToString().Length)
this.MaxLength++;
}
}
[Description("The maximum value allowed in the field.")]
[DefaultValue(100)]
public decimal Maximum
{
get {return this.m_Maximum;}
set
{
if (value < this.m_Minimum)
return;
this.m_Maximum = value;
this.MaxLength = value.ToString().Length;
int max = this.m_Minimum.ToString().Length;
if (this.m_Minimum < 0 && max > this.MaxLength)
this.MaxLength = max;
if (this.MaxLength > 2 && value.ToString().Length < this.m_Minimum.ToString().Length)
this.m_FormatString = new string('#', this.MaxLength - 2) + "0";
else if (this.MaxLength > 1)
this.m_FormatString = new string('#', this.MaxLength - 1) + "0";
else
this.m_FormatString = "0";
this.DecimalPlaces = this.m_DecimalPlaces;
}
}
[Description("The minimum value allowed in the field.")]
[DefaultValue(0)]
public decimal Minimum
{
get {return this.m_Minimum;}
set
{
if (value > this.m_Maximum)
return;
this.m_Minimum = value;
this.Maximum = this.m_Maximum;
}
}
#endregion
}
~~Bonnie
>Just when I'm really getting to like .Net, C# and some of the really cool things I can do like that marvelous free charting tool, I've got to go and try to input decimal numbers.
>
>Warning:
Flame on!>
>I've just spent several hours searching for a control to do decimal input as nicely as dBase II with no luck!
>
>If I have a format like ##0.0 and I type 10.1, I expect to
not have to position the cursor after the first blank space before typing! This is not unreasonable. I could do it in Fortran IV inputting from a teletype, for Pete's sake! In FoxBase I could type "10" and when I hit the decimal point the cursor would hop over to where it belonged and I could continue typing the fractional part.
And I didn't have to write three or four event handlers to get the job done either!>
>Comeon! Somebody has got to have done this right in a subclassed control?
>
>Is there any word yet?
Will the next version of .Net be as good as dBase II?>
>
Flame off