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 value;
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)
204 [RefreshProperties(RefreshProperties.All)]
205 public decimal Minimum {
213 if (maximum < 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");
265 OnValueChanged(EventArgs.Empty);
269 #endregion // Public Instance Properties
271 #region Public Instance Methods
272 public void BeginInit() {
273 suppress_validation++;
276 public override void DownButton() {
281 Value = Math.Max(minimum, unchecked(value - increment));
284 public void EndInit() {
285 suppress_validation--;
286 if (suppress_validation == 0)
290 public override string ToString() {
291 return string.Format("{0}: value {1} in range [{2}, {3}]", base.ToString(), value, minimum, maximum);
294 public override void UpButton() {
298 Value = Math.Min(maximum, unchecked(value + increment));
300 #endregion // Public Instance Methods
302 #region Protected Instance Methods
303 protected override AccessibleObject CreateAccessibilityInstance() {
304 AccessibleObject acc;
306 acc = new AccessibleObject(this);
307 acc.role = AccessibleRole.SpinButton;
312 protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
313 if ((ModifierKeys & ~Keys.Shift) != Keys.None) {
317 string acceptable = hexadecimal ? "\b-.,0123456789ABCDEF" : "\b-.,0123456789";
319 if (acceptable.IndexOf(e.KeyChar) < 0) {
320 // prevent the key from reaching the text box
324 base.OnTextBoxKeyPress(source, e);
327 protected virtual void OnValueChanged(EventArgs e) {
328 if (ValueChanged != null) {
329 ValueChanged(this, e);
333 protected void ParseEditText() {
337 string user_edit_text = Text.Replace(",", "").Trim();
340 value = decimal.Parse(user_edit_text);
344 for (int i=0; i < user_edit_text.Length; i++) {
345 int hex_digit = Convert.ToInt32(user_edit_text.Substring(i, 1), 16);
347 value = unchecked(value * 16M + (decimal)hex_digit);
351 if (value < minimum) {
355 if (value > maximum) {
359 OnValueChanged(EventArgs.Empty);
364 protected override void UpdateEditText() {
365 if (suppress_validation > 0)
369 ParseEditText(); // validate user input
372 // "N" and "F" differ only in that "N" includes commas
373 // every 3 digits to the left of the decimal and "F"
376 string format_string;
378 if (thousands_separator) {
384 format_string += decimal_places;
387 Text = value.ToString(format_string);
390 // Decimal.ToString doesn't know the "X" formatter, and
391 // converting it to an int is narrowing, so do it
394 int[] bits = decimal.GetBits(value);
396 bool negative = (bits[3] < 0);
398 int scale = (bits[3] >> 16) & 0x1F;
402 int[] radix = new int[4];
406 for (int i=0; i < scale; i++)
407 wide_number_multiply_by_10(radix);
411 while (!wide_number_less_than(bits, radix)) {
413 wide_number_multiply_by_16(radix);
416 if (num_chars == 0) {
422 StringBuilder chars = new StringBuilder();
427 for (int i=0; i < num_chars; i++) {
430 wide_number_divide_by_16(radix);
432 while (!wide_number_less_than(bits, radix)) { // greater than or equals
434 wide_number_subtract(bits, radix);
438 chars.Append((char)('0' + digit));
440 chars.Append((char)('A' + digit - 10));
445 Text = chars.ToString();
449 protected override void ValidateEditText() {
453 #endregion // Protected Instance Methods
456 public event EventHandler ValueChanged;
459 [EditorBrowsable(EditorBrowsableState.Never)]
460 public event EventHandler TextChanged;