1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2005 Novell, Inc.
23 // Jonathan Gilbert <logic@deltaq.org>
25 // Integration into MWF:
26 // Peter Bartok <pbartok@novell.com>
30 using System.Collections;
31 using System.ComponentModel;
33 using System.Globalization;
34 using System.Runtime.InteropServices;
36 using System.Windows.Forms;
38 namespace System.Windows.Forms {
39 [DefaultEvent("ValueChanged")]
40 [DefaultProperty("Value")]
42 [DefaultBindingProperty ("Value")]
43 [ClassInterface (ClassInterfaceType.AutoDispatch)]
46 public class NumericUpDown : UpDownBase, ISupportInitialize {
47 #region Local Variables
48 private bool suppress_validation;
49 private int decimal_places;
50 private bool hexadecimal;
51 private decimal increment;
52 private decimal maximum;
53 private decimal minimum;
54 private bool thousands_separator;
55 private decimal dvalue;
59 NumericUpDownAccelerationCollection accelerations;
60 // private long buttonPressedTicks;
61 //private bool isSpinning;
63 #endregion // Local Variables
65 #region Public Constructors
66 public NumericUpDown() {
67 suppress_validation = false;
73 thousands_separator = false;
77 #endregion // Public Constructors
79 #region Private Methods
80 void wide_number_multiply_by_10(int[] number) {
83 for (int i=0; i < number.Length; i++) {
84 long multiplication = unchecked(carry + 10 * (long)(uint)number[i]);
86 carry = multiplication >> 32;
88 number[i] = unchecked((int)multiplication);
92 void wide_number_multiply_by_16(int[] number) {
95 for (int i=0; i < number.Length; i++) {
96 int multiplication = unchecked(carry | (number[i] << 4));
98 carry = (number[i] >> 28) & 0x0F;
100 number[i] = multiplication;
104 void wide_number_divide_by_16(int[] number) {
107 for (int i=number.Length - 1; i >= 0; i--) {
108 int division = unchecked(carry | ((number[i] >> 4) & 0x0FFFFFFF));
110 carry = (number[i] << 28);
112 number[i] = division;
116 bool wide_number_less_than(int[] left, int[] right) {
118 for (int i=left.Length - 1; i >= 0; i--) {
119 uint leftvalue = (uint)left[i];
120 uint rightvalue = (uint)right[i];
122 if (leftvalue > rightvalue)
124 if (leftvalue < rightvalue)
133 void wide_number_subtract(int[] subtrahend, int[] minuend) {
137 for (int i=0; i < subtrahend.Length; i++) {
138 long subtrahendvalue = (uint)subtrahend[i];
139 long minuendvalue = (uint)minuend[i];
141 long result = subtrahendvalue - minuendvalue + carry;
145 result -= int.MinValue;
146 result -= int.MinValue;
151 subtrahend[i] = unchecked((int)result);
155 #endregion // Private Methods
157 #region Public Instance Properties
161 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
162 public NumericUpDownAccelerationCollection Accelerations {
164 if (accelerations == null)
165 accelerations = new NumericUpDownAccelerationCollection ();
166 return accelerations;
172 public int DecimalPlaces {
174 return decimal_places;
178 decimal_places = value;
183 [DefaultValue(false)]
184 public bool Hexadecimal {
195 public decimal Increment {
203 throw new ArgumentOutOfRangeException("value", value, "NumericUpDown increment cannot be negative");
210 [RefreshProperties(RefreshProperties.All)]
211 public decimal Maximum {
219 if (minimum > maximum)
222 if (dvalue > maximum)
227 [RefreshProperties(RefreshProperties.All)]
228 public decimal Minimum {
236 if (maximum < minimum)
239 if (dvalue < minimum)
246 [EditorBrowsable (EditorBrowsableState.Never)]
247 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
248 public new Padding Padding {
249 get { return Padding.Empty; }
256 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
257 [EditorBrowsable(EditorBrowsableState.Never)]
258 public override string Text {
268 [DefaultValue(false)]
270 public bool ThousandsSeparator {
272 return thousands_separator;
276 thousands_separator = value;
282 public decimal Value {
290 if (value != dvalue) {
291 if (!suppress_validation && ((value < minimum) || (value > maximum))) {
293 throw new ArgumentOutOfRangeException ("value", "NumericUpDown.Value must be within the specified Minimum and Maximum values");
295 throw new ArgumentException ("NumericUpDown.Value must be within the specified Minimum and Maximum values", "value");
300 OnValueChanged (EventArgs.Empty);
305 #endregion // Public Instance Properties
307 #region Public Instance Methods
308 public void BeginInit() {
309 suppress_validation = true;
312 public void EndInit() {
313 suppress_validation = false;
314 Value = Check (dvalue);
318 public override string ToString() {
319 return string.Format("{0}, Minimum = {1}, Maximum = {2}", base.ToString(), minimum, maximum);
322 public override void DownButton() {
327 Value = Math.Max (minimum, unchecked (dvalue - increment));
330 public override void UpButton() {
335 Value = Math.Min (maximum, unchecked (dvalue + increment));
337 #endregion // Public Instance Methods
339 #region Protected Instance Methods
340 protected override AccessibleObject CreateAccessibilityInstance() {
341 AccessibleObject acc;
343 acc = new AccessibleObject(this);
344 acc.role = AccessibleRole.SpinButton;
349 protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
350 if ((ModifierKeys & ~Keys.Shift) != Keys.None) {
354 NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
355 string pressedKey = e.KeyChar.ToString ();
357 if ((pressedKey != nfi.NegativeSign) && (pressedKey != nfi.NumberDecimalSeparator) &&
358 (pressedKey != nfi.NumberGroupSeparator)) {
359 string acceptable = hexadecimal ? "\b0123456789abcdefABCDEF" : "\b0123456789";
360 if (acceptable.IndexOf (e.KeyChar) == -1) {
361 // FIXME: produce beep to signal that "invalid" key was pressed
362 // prevent the key from reaching the text box
367 base.OnTextBoxKeyPress(source, e);
370 protected virtual void OnValueChanged(EventArgs e) {
371 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
376 protected void ParseEditText () {
379 Value = decimal.Parse (Text, CultureInfo.CurrentCulture);
382 Value = Check (Convert.ToDecimal (Convert.ToInt32 (Text, 16)));
384 Value = Check (Convert.ToDecimal (Convert.ToInt32 (Text, 10)));
394 private decimal Check (decimal val)
408 protected override void UpdateEditText () {
409 if (suppress_validation)
417 // "N" and "F" differ only in that "N" includes commas
418 // every 3 digits to the left of the decimal and "F"
421 string format_string;
423 if (thousands_separator) {
429 format_string += decimal_places;
431 Text = dvalue.ToString (format_string, CultureInfo.CurrentCulture);
434 // Decimal.ToString doesn't know the "X" formatter, and
435 // converting it to an int is narrowing, so do it
438 int[] bits = decimal.GetBits (dvalue);
440 bool negative = (bits[3] < 0);
442 int scale = (bits[3] >> 16) & 0x1F;
446 int[] radix = new int[4];
450 for (int i = 0; i < scale; i++)
451 wide_number_multiply_by_10 (radix);
455 while (!wide_number_less_than (bits, radix)) {
457 wide_number_multiply_by_16 (radix);
460 if (num_chars == 0) {
464 StringBuilder chars = new StringBuilder ();
469 for (int i = 0; i < num_chars; i++) {
472 wide_number_divide_by_16 (radix);
474 while (!wide_number_less_than (bits, radix)) { // greater than or equals
476 wide_number_subtract (bits, radix);
480 chars.Append ((char) ('0' + digit));
482 chars.Append ((char) ('A' + digit - 10));
486 Text = chars.ToString ();
491 protected override void ValidateEditText() {
497 protected override void OnLostFocus(EventArgs e) {
503 protected override void OnKeyUp (KeyEventArgs e)
505 // isSpinning = false;
509 protected override void OnKeyDown (KeyEventArgs e)
511 // buttonPressedTicks = DateTime.Now.Ticks;
512 // isSpinning = true;
516 #endregion // Protected Instance Methods
521 [EditorBrowsable (EditorBrowsableState.Never)]
522 public new event EventHandler PaddingChanged {
523 add { base.PaddingChanged += value; }
524 remove { base.PaddingChanged -= value; }
528 static object ValueChangedEvent = new object ();
530 public event EventHandler ValueChanged {
531 add { Events.AddHandler (ValueChangedEvent, value); }
532 remove { Events.RemoveHandler (ValueChangedEvent, value); }
536 [EditorBrowsable(EditorBrowsableState.Never)]
537 public new event EventHandler TextChanged {
538 add { base.TextChanged += value; }
539 remove { base.TextChanged -= value; }