NumericUpDown.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  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.
  19. //
  20. // Copyright (c) 2005 Novell, Inc.
  21. //
  22. // Authors:
  23. // Jonathan Gilbert <[email protected]>
  24. //
  25. // Integration into MWF:
  26. // Peter Bartok <[email protected]>
  27. //
  28. // COMPLETE
  29. using System;
  30. using System.Collections;
  31. using System.ComponentModel;
  32. using System.Drawing;
  33. using System.Globalization;
  34. using System.Runtime.InteropServices;
  35. using System.Text;
  36. using System.Windows.Forms;
  37. namespace System.Windows.Forms {
  38. [DefaultEvent("ValueChanged")]
  39. [DefaultProperty("Value")]
  40. public class NumericUpDown : UpDownBase, ISupportInitialize {
  41. #region Local Variables
  42. private int suppress_validation;
  43. private int decimal_places;
  44. private bool hexadecimal;
  45. private decimal increment;
  46. private decimal maximum;
  47. private decimal minimum;
  48. private bool thousands_separator;
  49. private decimal dvalue;
  50. #endregion // Local Variables
  51. #region Public Constructors
  52. public NumericUpDown() {
  53. suppress_validation = 0;
  54. decimal_places = 0;
  55. hexadecimal = false;
  56. increment = 1M;
  57. maximum = 100M;
  58. minimum = 0.0M;
  59. thousands_separator = false;
  60. }
  61. #endregion // Public Constructors
  62. #region Private Methods
  63. void wide_number_multiply_by_10(int[] number) {
  64. long carry = 0;
  65. for (int i=0; i < number.Length; i++) {
  66. long multiplication = unchecked(carry + 10 * (long)(uint)number[i]);
  67. carry = multiplication >> 32;
  68. number[i] = unchecked((int)multiplication);
  69. }
  70. }
  71. void wide_number_multiply_by_16(int[] number) {
  72. int carry = 0;
  73. for (int i=0; i < number.Length; i++) {
  74. int multiplication = unchecked(carry | (number[i] << 4));
  75. carry = (number[i] >> 28) & 0x0F;
  76. number[i] = multiplication;
  77. }
  78. }
  79. void wide_number_divide_by_16(int[] number) {
  80. int carry = 0;
  81. for (int i=number.Length - 1; i >= 0; i--) {
  82. int division = unchecked(carry | ((number[i] >> 4) & 0x0FFFFFFF));
  83. carry = (number[i] << 28);
  84. number[i] = division;
  85. }
  86. }
  87. bool wide_number_less_than(int[] left, int[] right) {
  88. unchecked {
  89. for (int i=left.Length - 1; i >= 0; i--) {
  90. uint leftvalue = (uint)left[i];
  91. uint rightvalue = (uint)right[i];
  92. if (leftvalue > rightvalue)
  93. return false;
  94. if (leftvalue < rightvalue)
  95. return true;
  96. }
  97. }
  98. // equal
  99. return false;
  100. }
  101. void wide_number_subtract(int[] subtrahend, int[] minuend) {
  102. long carry = 0;
  103. unchecked {
  104. for (int i=0; i < subtrahend.Length; i++) {
  105. long subtrahendvalue = (uint)subtrahend[i];
  106. long minuendvalue = (uint)minuend[i];
  107. long result = subtrahendvalue - minuendvalue + carry;
  108. if (result < 0) {
  109. carry = -1;
  110. result -= int.MinValue;
  111. result -= int.MinValue;
  112. }
  113. else
  114. carry = 0;
  115. subtrahend[i] = unchecked((int)result);
  116. }
  117. }
  118. }
  119. #endregion // Private Methods
  120. #region Public Instance Properties
  121. [DefaultValue(0)]
  122. public int DecimalPlaces {
  123. get {
  124. return decimal_places;
  125. }
  126. set {
  127. decimal_places = value;
  128. UpdateEditText();
  129. }
  130. }
  131. [DefaultValue(false)]
  132. public bool Hexadecimal {
  133. get {
  134. return hexadecimal;
  135. }
  136. set {
  137. hexadecimal = value;
  138. UpdateEditText();
  139. }
  140. }
  141. public decimal Increment {
  142. get {
  143. return increment;
  144. }
  145. set {
  146. if (value < 0) {
  147. throw new ArgumentOutOfRangeException("value", value, "NumericUpDown increment cannot be negative");
  148. }
  149. increment = value;
  150. }
  151. }
  152. [RefreshProperties(RefreshProperties.All)]
  153. public decimal Maximum {
  154. get {
  155. return maximum;
  156. }
  157. set {
  158. maximum = value;
  159. if (minimum > maximum)
  160. minimum = maximum;
  161. if (dvalue > maximum)
  162. Value = maximum;
  163. }
  164. }
  165. [RefreshProperties(RefreshProperties.All)]
  166. public decimal Minimum {
  167. get {
  168. return minimum;
  169. }
  170. set {
  171. minimum = value;
  172. if (maximum < minimum)
  173. maximum = minimum;
  174. if (dvalue < minimum)
  175. Value = minimum;
  176. }
  177. }
  178. [Bindable(false)]
  179. [Browsable(false)]
  180. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  181. [EditorBrowsable(EditorBrowsableState.Never)]
  182. public override string Text {
  183. get {
  184. return base.Text;
  185. }
  186. set {
  187. base.Text = value;
  188. }
  189. }
  190. [DefaultValue(false)]
  191. [Localizable(true)]
  192. public bool ThousandsSeparator {
  193. get {
  194. return thousands_separator;
  195. }
  196. set {
  197. thousands_separator = value;
  198. UpdateEditText();
  199. }
  200. }
  201. [Bindable(true)]
  202. public decimal Value {
  203. get {
  204. return dvalue;
  205. }
  206. set {
  207. if (suppress_validation <= 0) {
  208. if ((value < minimum) || (value > maximum)) {
  209. throw new ArgumentException("NumericUpDown.Value must be within the specified Minimum and Maximum values", "value");
  210. }
  211. }
  212. if (value != dvalue) {
  213. dvalue = value;
  214. OnValueChanged(EventArgs.Empty);
  215. UpdateEditText();
  216. }
  217. }
  218. }
  219. #endregion // Public Instance Properties
  220. #region Public Instance Methods
  221. public void BeginInit() {
  222. suppress_validation++;
  223. }
  224. public override void DownButton() {
  225. if (UserEdit) {
  226. ParseEditText();
  227. }
  228. Value = Math.Max(minimum, unchecked(dvalue - increment));
  229. }
  230. public void EndInit() {
  231. suppress_validation--;
  232. if (suppress_validation == 0)
  233. UpdateEditText ();
  234. }
  235. public override string ToString() {
  236. return string.Format("{0}, Minimum = {1}, Maximum = {2}", base.ToString(), minimum, maximum);
  237. }
  238. public override void UpButton() {
  239. if (UserEdit)
  240. ParseEditText();
  241. Value = Math.Min(maximum, unchecked(dvalue + increment));
  242. }
  243. #endregion // Public Instance Methods
  244. #region Protected Instance Methods
  245. protected override AccessibleObject CreateAccessibilityInstance() {
  246. AccessibleObject acc;
  247. acc = new AccessibleObject(this);
  248. acc.role = AccessibleRole.SpinButton;
  249. return acc;
  250. }
  251. protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
  252. if ((ModifierKeys & ~Keys.Shift) != Keys.None) {
  253. return;
  254. }
  255. NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
  256. string pressedKey = e.KeyChar.ToString ();
  257. if ((pressedKey != nfi.NegativeSign) && (pressedKey != nfi.NumberDecimalSeparator) &&
  258. (pressedKey != nfi.NumberGroupSeparator)) {
  259. string acceptable = hexadecimal ? "\b0123456789abcdefABCDEF" : "\b0123456789";
  260. if (acceptable.IndexOf (e.KeyChar) == -1) {
  261. // FIXME: produce beep to signal that "invalid" key was pressed
  262. // prevent the key from reaching the text box
  263. e.Handled = true;
  264. }
  265. }
  266. base.OnTextBoxKeyPress(source, e);
  267. }
  268. protected virtual void OnValueChanged(EventArgs e) {
  269. if (ValueChanged != null) {
  270. ValueChanged(this, e);
  271. }
  272. }
  273. protected void ParseEditText() {
  274. UserEdit = false;
  275. try {
  276. string user_edit_text = Text;
  277. if (!hexadecimal) {
  278. dvalue = decimal.Parse(user_edit_text, CultureInfo.CurrentCulture);
  279. } else {
  280. #if NET_1_1
  281. dvalue = Convert.ToDecimal (Convert.ToInt32 (user_edit_text, 16));
  282. #else
  283. dvalue = Convert.ToDecimal (Convert.ToInt32 (user_edit_text, 10));
  284. #endif
  285. }
  286. if (dvalue < minimum) {
  287. dvalue = minimum;
  288. }
  289. if (dvalue > maximum) {
  290. dvalue = maximum;
  291. }
  292. OnValueChanged(EventArgs.Empty);
  293. }
  294. catch {}
  295. }
  296. protected override void UpdateEditText() {
  297. if (suppress_validation > 0)
  298. return;
  299. if (UserEdit)
  300. ParseEditText(); // validate user input
  301. if (!hexadecimal) {
  302. // "N" and "F" differ only in that "N" includes commas
  303. // every 3 digits to the left of the decimal and "F"
  304. // does not.
  305. string format_string;
  306. if (thousands_separator) {
  307. format_string = "N";
  308. } else {
  309. format_string = "F";
  310. }
  311. format_string += decimal_places;
  312. ChangingText = true;
  313. Text = dvalue.ToString(format_string, CultureInfo.CurrentCulture);
  314. }
  315. else {
  316. // Decimal.ToString doesn't know the "X" formatter, and
  317. // converting it to an int is narrowing, so do it
  318. // manually...
  319. int[] bits = decimal.GetBits(dvalue);
  320. bool negative = (bits[3] < 0);
  321. int scale = (bits[3] >> 16) & 0x1F;
  322. bits[3] = 0;
  323. int[] radix = new int[4];
  324. radix[0] = 1;
  325. for (int i=0; i < scale; i++)
  326. wide_number_multiply_by_10(radix);
  327. int num_chars = 0;
  328. while (!wide_number_less_than(bits, radix)) {
  329. num_chars++;
  330. wide_number_multiply_by_16(radix);
  331. }
  332. if (num_chars == 0) {
  333. ChangingText = true;
  334. Text = "0";
  335. return;
  336. }
  337. StringBuilder chars = new StringBuilder();
  338. if (negative)
  339. chars.Append('-');
  340. for (int i=0; i < num_chars; i++) {
  341. int digit = 0;
  342. wide_number_divide_by_16(radix);
  343. while (!wide_number_less_than(bits, radix)) { // greater than or equals
  344. digit++;
  345. wide_number_subtract(bits, radix);
  346. }
  347. if (digit < 10) {
  348. chars.Append((char)('0' + digit));
  349. } else {
  350. chars.Append((char)('A' + digit - 10));
  351. }
  352. }
  353. ChangingText = true;
  354. Text = chars.ToString();
  355. }
  356. }
  357. protected override void ValidateEditText() {
  358. ParseEditText();
  359. UpdateEditText();
  360. }
  361. #if NET_2_0
  362. protected override void OnLostFocus(EventArgs e) {
  363. base.OnLostFocus(e);
  364. if (this.UserEdit)
  365. this.UpdateEditText();
  366. }
  367. #endif
  368. #endregion // Protected Instance Methods
  369. #region Events
  370. public event EventHandler ValueChanged;
  371. [Browsable(false)]
  372. [EditorBrowsable(EditorBrowsableState.Never)]
  373. public event EventHandler TextChanged;
  374. #endregion // Events
  375. }
  376. }