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>
32 using System.Collections;
33 using System.ComponentModel;
35 using System.Runtime.InteropServices;
37 using System.Windows.Forms;
39 namespace System.Windows.Forms {
40 [DefaultEvent("ValueChanged")]
41 [DefaultProperty("Value")]
42 public class NumericUpDown : UpDownBase, ISupportInitialize {
43 #region Local Variables
44 private int suppress_validation;
45 private int decimal_places;
46 private bool hexadecimal;
47 private decimal increment;
48 private decimal maximum;
49 private decimal minimum;
50 private bool thousands_separator;
51 private decimal dvalue;
52 #endregion // Local Variables
54 #region Public Constructors
55 public NumericUpDown() {
56 suppress_validation = 0;
62 thousands_separator = false;
64 #endregion // Public Constructors
66 #region Private Methods
67 void wide_number_multiply_by_10(int[] number) {
70 for (int i=0; i < number.Length; i++) {
71 long multiplication = unchecked(carry + 10 * (long)(uint)number[i]);
73 carry = multiplication >> 32;
75 number[i] = unchecked((int)multiplication);
79 void wide_number_multiply_by_16(int[] number) {
82 for (int i=0; i < number.Length; i++) {
83 int multiplication = unchecked(carry | (number[i] << 4));
85 carry = (number[i] >> 28) & 0x0F;
87 number[i] = multiplication;
91 void wide_number_divide_by_16(int[] number) {
94 for (int i=number.Length - 1; i >= 0; i--) {
95 int division = unchecked(carry | ((number[i] >> 4) & 0x0FFFFFFF));
97 carry = (number[i] << 28);
103 bool wide_number_less_than(int[] left, int[] right) {
105 for (int i=left.Length - 1; i >= 0; i--) {
106 uint leftvalue = (uint)left[i];
107 uint rightvalue = (uint)right[i];
109 if (leftvalue > rightvalue)
111 if (leftvalue < rightvalue)
120 void wide_number_subtract(int[] subtrahend, int[] minuend) {
124 for (int i=0; i < subtrahend.Length; i++) {
125 long subtrahendvalue = (uint)subtrahend[i];
126 long minuendvalue = (uint)minuend[i];
128 long result = subtrahendvalue - minuendvalue + carry;
132 result -= int.MinValue;
133 result -= int.MinValue;
138 subtrahend[i] = unchecked((int)result);
142 #endregion // Private Methods
144 #region Public Instance Properties
146 public int DecimalPlaces {
148 return decimal_places;
152 decimal_places = value;
159 [DefaultValue(false)]
160 public bool Hexadecimal {
173 public decimal Increment {
180 throw new ArgumentOutOfRangeException("value", value, "NumericUpDown increment cannot be negative");
187 [RefreshProperties(RefreshProperties.All)]
188 public decimal Maximum {
196 if (minimum > maximum)
199 if (dvalue > maximum)
204 [RefreshProperties(RefreshProperties.All)]
205 public decimal Minimum {
213 if (maximum < minimum)
216 if (dvalue < minimum)
223 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
224 [EditorBrowsable(EditorBrowsableState.Never)]
225 public override string Text {
235 [DefaultValue(false)]
237 public bool ThousandsSeparator {
239 return thousands_separator;
243 thousands_separator = value;
252 public decimal Value {
258 if (suppress_validation <= 0) {
259 if ((value < minimum) || (value > maximum)) {
260 throw new ArgumentException("NumericUpDown.Value must be within the specified Minimum and Maximum values", "value");
263 if (value != dvalue) {
265 OnValueChanged(EventArgs.Empty);
270 #endregion // Public Instance Properties
272 #region Public Instance Methods
273 public void BeginInit() {
274 suppress_validation++;
277 public override void DownButton() {
282 Value = Math.Max(minimum, unchecked(dvalue - increment));
285 public void EndInit() {
286 suppress_validation--;
287 if (suppress_validation == 0)
291 public override string ToString() {
292 return string.Format("{0}: value {1} in range [{2}, {3}]", base.ToString(), dvalue, minimum, maximum);
295 public override void UpButton() {
299 Value = Math.Min(maximum, unchecked(dvalue + increment));
301 #endregion // Public Instance Methods
303 #region Protected Instance Methods
304 protected override AccessibleObject CreateAccessibilityInstance() {
305 AccessibleObject acc;
307 acc = new AccessibleObject(this);
308 acc.role = AccessibleRole.SpinButton;
313 protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
314 if ((ModifierKeys & ~Keys.Shift) != Keys.None) {
318 string acceptable = hexadecimal ? "\b-.,0123456789ABCDEF" : "\b-.,0123456789";
320 if (acceptable.IndexOf(e.KeyChar) < 0) {
321 // prevent the key from reaching the text box
325 base.OnTextBoxKeyPress(source, e);
328 protected virtual void OnValueChanged(EventArgs e) {
329 if (ValueChanged != null) {
330 ValueChanged(this, e);
334 protected void ParseEditText() {
338 string user_edit_text = Text.Replace(",", "").Trim();
341 dvalue = decimal.Parse(user_edit_text);
345 for (int i=0; i < user_edit_text.Length; i++) {
346 int hex_digit = Convert.ToInt32(user_edit_text.Substring(i, 1), 16);
348 dvalue = unchecked(dvalue * 16M + (decimal)hex_digit);
352 if (dvalue < minimum) {
356 if (dvalue > maximum) {
360 OnValueChanged(EventArgs.Empty);
365 protected override void UpdateEditText() {
366 if (suppress_validation > 0)
370 ParseEditText(); // validate user input
373 // "N" and "F" differ only in that "N" includes commas
374 // every 3 digits to the left of the decimal and "F"
377 string format_string;
379 if (thousands_separator) {
385 format_string += decimal_places;
388 Text = dvalue.ToString(format_string);
391 // Decimal.ToString doesn't know the "X" formatter, and
392 // converting it to an int is narrowing, so do it
395 int[] bits = decimal.GetBits(dvalue);
397 bool negative = (bits[3] < 0);
399 int scale = (bits[3] >> 16) & 0x1F;
403 int[] radix = new int[4];
407 for (int i=0; i < scale; i++)
408 wide_number_multiply_by_10(radix);
412 while (!wide_number_less_than(bits, radix)) {
414 wide_number_multiply_by_16(radix);
417 if (num_chars == 0) {
423 StringBuilder chars = new StringBuilder();
428 for (int i=0; i < num_chars; i++) {
431 wide_number_divide_by_16(radix);
433 while (!wide_number_less_than(bits, radix)) { // greater than or equals
435 wide_number_subtract(bits, radix);
439 chars.Append((char)('0' + digit));
441 chars.Append((char)('A' + digit - 10));
446 Text = chars.ToString();
450 protected override void ValidateEditText() {
454 #endregion // Protected Instance Methods
457 public event EventHandler ValueChanged;
460 [EditorBrowsable(EditorBrowsableState.Never)]
461 public event EventHandler TextChanged;