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 {
227 return base.txtView.Text;
231 base.txtView.Text = value;
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--;
288 public override string ToString() {
289 return string.Format("{0}: value {1} in range [{2}, {3}]", base.ToString(), value, minimum, maximum);
292 public override void UpButton() {
296 Value = Math.Min(maximum, unchecked(value + increment));
298 #endregion // Public Instance Methods
300 #region Protected Instance Methods
301 protected override AccessibleObject CreateAccessibilityInstance() {
302 AccessibleObject acc;
304 acc = new AccessibleObject(this);
305 acc.role = AccessibleRole.SpinButton;
310 protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
311 if ((ModifierKeys & ~Keys.Shift) != Keys.None) {
315 string acceptable = hexadecimal ? "\b-.,0123456789ABCDEF" : "\b-.,0123456789";
317 if (acceptable.IndexOf(e.KeyChar) < 0) {
318 // prevent the key from reaching the text box
322 base.OnTextBoxKeyPress(source, e);
325 protected virtual void OnValueChanged(EventArgs e) {
326 if (ValueChanged != null) {
327 ValueChanged(this, e);
331 protected void ParseEditText() {
335 string user_edit_text = Text.Replace(",", "").Trim();
338 value = decimal.Parse(user_edit_text);
342 for (int i=0; i < user_edit_text.Length; i++) {
343 int hex_digit = Convert.ToInt32(user_edit_text.Substring(i, 1), 16);
345 value = unchecked(value * 16M + (decimal)hex_digit);
349 if (value < minimum) {
353 if (value > maximum) {
357 OnValueChanged(EventArgs.Empty);
362 protected override void UpdateEditText() {
363 if (suppress_validation > 0)
367 ParseEditText(); // validate user input
370 // "N" and "F" differ only in that "N" includes commas
371 // every 3 digits to the left of the decimal and "F"
374 string format_string;
376 if (thousands_separator) {
382 format_string += decimal_places;
385 Text = value.ToString(format_string);
388 // Decimal.ToString doesn't know the "X" formatter, and
389 // converting it to an int is narrowing, so do it
392 int[] bits = decimal.GetBits(value);
394 bool negative = (bits[3] < 0);
396 int scale = (bits[3] >> 16) & 0x1F;
400 int[] radix = new int[4];
404 for (int i=0; i < scale; i++)
405 wide_number_multiply_by_10(radix);
409 while (!wide_number_less_than(bits, radix)) {
411 wide_number_multiply_by_16(radix);
414 if (num_chars == 0) {
420 StringBuilder chars = new StringBuilder();
425 for (int i=0; i < num_chars; i++) {
428 wide_number_divide_by_16(radix);
430 while (!wide_number_less_than(bits, radix)) { // greater than or equals
432 wide_number_subtract(bits, radix);
436 chars.Append((char)('0' + digit));
438 chars.Append((char)('A' + digit - 10));
443 Text = chars.ToString();
447 protected override void ValidateEditText() {
451 #endregion // Protected Instance Methods
454 public event EventHandler ValueChanged;
457 [EditorBrowsable(EditorBrowsableState.Never)]
458 public event EventHandler TextChanged;