|
//------------------------------------------------------------------------------
// <copyright file="NumericUpDown.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms {
using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Windows.Forms.Internal;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms.Layout;
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown"]/*' />
/// <devdoc>
/// <para>Represents a Windows up-down control that displays numeric values.</para>
/// </devdoc>
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
DefaultProperty("Value"),
DefaultEvent("ValueChanged"),
DefaultBindingProperty("Value"),
SRDescription(SR.DescriptionNumericUpDown)
]
public class NumericUpDown : UpDownBase, ISupportInitialize {
private static readonly Decimal DefaultValue = Decimal.Zero;
private static readonly Decimal DefaultMinimum = Decimal.Zero;
private static readonly Decimal DefaultMaximum = (Decimal)100.0;
private const int DefaultDecimalPlaces = 0;
private static readonly Decimal DefaultIncrement = Decimal.One;
private const bool DefaultThousandsSeparator = false;
private const bool DefaultHexadecimal = false;
private const int InvalidValue = -1;
//////////////////////////////////////////////////////////////
// Member variables
//
//////////////////////////////////////////////////////////////
/// <devdoc>
/// The number of decimal places to display.
/// </devdoc>
private int decimalPlaces = DefaultDecimalPlaces;
/// <devdoc>
/// The amount to increment by.
/// </devdoc>
private Decimal increment = DefaultIncrement;
// Display the thousands separator?
private bool thousandsSeparator = DefaultThousandsSeparator;
// Minimum and maximum values
private Decimal minimum = DefaultMinimum;
private Decimal maximum = DefaultMaximum;
// Hexadecimal
private bool hexadecimal = DefaultHexadecimal;
// Internal storage of the current value
private Decimal currentValue = DefaultValue;
private bool currentValueChanged;
// Event handler for the onValueChanged event
private EventHandler onValueChanged = null;
// Disable value range checking while initializing the control
private bool initializing = false;
// Provides for finer acceleration behavior.
private NumericUpDownAccelerationCollection accelerations;
// the current NumericUpDownAcceleration object.
private int accelerationsCurrentIndex;
// Used to calculate the time elapsed since the up/down button was pressed,
// to know when to get the next entry in the accelaration table.
private long buttonPressedStartTime;
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.NumericUpDown"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "0" is the default value for numeric up down.
// So we don't have to localize it.
]
public NumericUpDown() : base() {
// this class overrides GetPreferredSizeCore, let Control automatically cache the result
SetState2(STATE2_USEPREFERREDSIZECACHE, true);
Text = "0";
StopAcceleration();
}
//////////////////////////////////////////////////////////////
// Properties
//
//////////////////////////////////////////////////////////////
/// <devdoc>
/// Specifies the acceleration information.
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public NumericUpDownAccelerationCollection Accelerations {
get {
if( this.accelerations == null ){
this.accelerations = new NumericUpDownAccelerationCollection();
}
return this.accelerations;
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.DecimalPlaces"]/*' />
/// <devdoc>
/// <para>Gets or sets the number of decimal places to display in the up-down control.</para>
/// </devdoc>
[
SRCategory(SR.CatData),
DefaultValue(NumericUpDown.DefaultDecimalPlaces),
SRDescription(SR.NumericUpDownDecimalPlacesDescr)
]
public int DecimalPlaces {
get {
return decimalPlaces;
}
set {
if (value < 0 || value > 99) {
throw new ArgumentOutOfRangeException("DecimalPlaces", SR.GetString(SR.InvalidBoundArgument, "DecimalPlaces", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture), "99"));
}
decimalPlaces = value;
UpdateEditText();
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.Hexadecimal"]/*' />
/// <devdoc>
/// <para>Gets or
/// sets a value indicating whether the up-down control should
/// display the value it contains in hexadecimal format.</para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
DefaultValue(NumericUpDown.DefaultHexadecimal),
SRDescription(SR.NumericUpDownHexadecimalDescr)
]
public bool Hexadecimal {
get {
return hexadecimal;
}
set {
hexadecimal = value;
UpdateEditText();
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.Increment"]/*' />
/// <devdoc>
/// <para>Gets or sets the value
/// to increment or
/// decrement the up-down control when the up or down buttons are clicked.</para>
/// </devdoc>
[
SRCategory(SR.CatData),
SRDescription(SR.NumericUpDownIncrementDescr)
]
public Decimal Increment {
get {
if (this.accelerationsCurrentIndex != InvalidValue) {
return this.Accelerations[this.accelerationsCurrentIndex].Increment;
}
return this.increment;
}
set {
if (value < (Decimal)0.0) {
throw new ArgumentOutOfRangeException("Increment", SR.GetString(SR.InvalidArgument, "Increment", value.ToString(CultureInfo.CurrentCulture)));
}
else {
this.increment = value;
}
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.Maximum"]/*' />
/// <devdoc>
/// <para>Gets or sets the maximum value for the up-down control.</para>
/// </devdoc>
[
SRCategory(SR.CatData),
RefreshProperties(RefreshProperties.All),
SRDescription(SR.NumericUpDownMaximumDescr)
]
public Decimal Maximum {
get {
return maximum;
}
set {
maximum = value;
if (minimum > maximum) {
minimum = maximum;
}
Value = Constrain(currentValue);
Debug.Assert(maximum == value, "Maximum != what we just set it to!");
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.Minimum"]/*' />
/// <devdoc>
/// <para>Gets or sets the minimum allowed value for the up-down control.</para>
/// </devdoc>
[
SRCategory(SR.CatData),
RefreshProperties(RefreshProperties.All),
SRDescription(SR.NumericUpDownMinimumDescr)
]
public Decimal Minimum {
get {
return minimum;
}
set {
minimum = value;
if (minimum > maximum) {
maximum = value;
}
Value = Constrain(currentValue);
Debug.Assert(minimum.Equals(value), "Minimum != what we just set it to!");
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.Padding"]/*' />
/// <devdoc>
/// <para>
/// <para>[To be supplied.]</para>
/// </para>
/// </devdoc>
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public new Padding Padding {
get { return base.Padding; }
set { base.Padding = value;}
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
new public event EventHandler PaddingChanged {
add { base.PaddingChanged += value; }
remove { base.PaddingChanged -= value; }
}
/// <devdoc>
/// Determines whether the UpDownButtons have been pressed for enough time to activate acceleration.
/// </devdoc>
private bool Spinning {
get{
return this.accelerations != null && this.buttonPressedStartTime != InvalidValue;
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.Text"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// The text displayed in the control.
/// </para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
Bindable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
// We're just overriding this to make it non-browsable.
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.TextChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler TextChanged {
add {
base.TextChanged += value;
}
remove {
base.TextChanged -= value;
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.ThousandsSeparator"]/*' />
/// <devdoc>
/// <para>Gets or sets a value indicating whether a thousands
/// separator is displayed in the up-down control when appropriate.</para>
/// </devdoc>
[
SRCategory(SR.CatData),
DefaultValue(NumericUpDown.DefaultThousandsSeparator),
Localizable(true),
SRDescription(SR.NumericUpDownThousandsSeparatorDescr)
]
public bool ThousandsSeparator {
get {
return thousandsSeparator;
}
set {
thousandsSeparator = value;
UpdateEditText();
}
}
/*
* The current value of the control
*/
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.Value"]/*' />
/// <devdoc>
/// <para>Gets or sets the value
/// assigned to the up-down control.</para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
Bindable(true),
SRDescription(SR.NumericUpDownValueDescr)
]
public Decimal Value {
get {
if (UserEdit) {
ValidateEditText();
}
return currentValue;
}
set {
if (value != currentValue) {
if (!initializing && ((value < minimum) || (value > maximum))) {
throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", value.ToString(CultureInfo.CurrentCulture), "'Minimum'", "'Maximum'"));
}
else {
currentValue = value;
OnValueChanged(EventArgs.Empty);
currentValueChanged = true;
UpdateEditText();
}
}
}
}
//////////////////////////////////////////////////////////////
// Methods
//
//////////////////////////////////////////////////////////////
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.ValueChanged"]/*' />
/// <devdoc>
/// <para>
/// Occurs when the <see cref='System.Windows.Forms.NumericUpDown.Value'/> property has been changed in some way.
/// </para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.NumericUpDownOnValueChangedDescr)]
public event EventHandler ValueChanged {
add {
onValueChanged += value;
}
remove {
onValueChanged -= value;
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.BeginInit"]/*' />
/// <internalonly/>
/// <devdoc>
/// Handles tasks required when the control is being initialized.
/// </devdoc>
public void BeginInit() {
initializing = true;
}
//
// Returns the provided value constrained to be within the min and max.
//
private Decimal Constrain(Decimal value) {
Debug.Assert(minimum <= maximum,
"minimum > maximum");
if (value < minimum) {
value = minimum;
}
if (value > maximum) {
value = maximum;
}
return value;
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.CreateAccessibilityInstance"]/*' />
protected override AccessibleObject CreateAccessibilityInstance() {
return new NumericUpDownAccessibleObject(this);
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.DownButton"]/*' />
/// <devdoc>
/// <para>
/// Decrements the value of the up-down control.
/// </para>
/// </devdoc>
public override void DownButton() {
SetNextAcceleration();
if (UserEdit) {
ParseEditText();
}
Decimal newValue = currentValue;
// Operations on Decimals can throw OverflowException.
//
try{
newValue -= this.Increment;
if (newValue < minimum){
newValue = minimum;
if( this.Spinning){
StopAcceleration();
}
}
}
catch( OverflowException ){
newValue = minimum;
}
Value = newValue;
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.EndInit"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// Called when initialization of the control is complete.
/// </para>
/// </devdoc>
public void EndInit() {
initializing = false;
Value = Constrain(currentValue);
UpdateEditText();
}
/// <devdoc>
/// Overridden to set/reset acceleration variables.
/// </devdoc>
protected override void OnKeyDown(KeyEventArgs e) {
if (base.InterceptArrowKeys && (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) && !this.Spinning) {
StartAcceleration();
}
base.OnKeyDown(e);
}
/// <devdoc>
/// Overridden to set/reset acceleration variables.
/// </devdoc>
protected override void OnKeyUp(KeyEventArgs e) {
if (base.InterceptArrowKeys && (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down)) {
StopAcceleration();
}
base.OnKeyUp(e);
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.OnTextBoxKeyPress"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// Restricts the entry of characters to digits (including hex), the negative sign,
/// the decimal point, and editing keystrokes (backspace).
/// </para>
/// </devdoc>
protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
base.OnTextBoxKeyPress(source, e);
NumberFormatInfo numberFormatInfo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
string decimalSeparator = numberFormatInfo.NumberDecimalSeparator;
string groupSeparator = numberFormatInfo.NumberGroupSeparator;
string negativeSign = numberFormatInfo.NegativeSign;
string keyInput = e.KeyChar.ToString();
if (Char.IsDigit(e.KeyChar)) {
// Digits are OK
}
else if (keyInput.Equals(decimalSeparator) || keyInput.Equals(groupSeparator) ||
keyInput.Equals(negativeSign)) {
// Decimal separator is OK
}
else if (e.KeyChar == '\b') {
// Backspace key is OK
}
else if (Hexadecimal && ((e.KeyChar >= 'a' && e.KeyChar <= 'f') || e.KeyChar >= 'A' && e.KeyChar <= 'F')) {
// Hexadecimal digits are OK
}
else if ((ModifierKeys & (Keys.Control | Keys.Alt)) != 0) {
// Let the edit control handle control and alt key combinations
}
else {
// Eat this invalid key and beep
e.Handled = true;
SafeNativeMethods.MessageBeep(0);
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.OnValueChanged"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.NumericUpDown.OnValueChanged'/> event.</para>
/// </devdoc>
protected virtual void OnValueChanged(EventArgs e) {
// Call the event handler
if (onValueChanged != null) {
onValueChanged(this, e);
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.OnLostFocus"]/*' />
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
if (UserEdit)
{
UpdateEditText();
}
}
/// <devdoc>
/// Overridden to start/end acceleration.
/// </devdoc>
internal override void OnStartTimer() {
StartAcceleration();
}
/// <devdoc>
/// Overridden to start/end acceleration.
/// </devdoc>
internal override void OnStopTimer() {
StopAcceleration();
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.ParseEditText"]/*' />
/// <devdoc>
/// <para>
/// Converts the text displayed in the up-down control to a
/// numeric value and evaluates it.
/// </para>
/// </devdoc>
protected void ParseEditText() {
Debug.Assert(UserEdit == true, "ParseEditText() - UserEdit == false");
try {
// VSWhidbey 173332: Verify that the user is not starting the string with a "-"
// before attempting to set the Value property since a "-" is a valid character with
// which to start a string representing a negative number.
if (!string.IsNullOrEmpty(Text) &&
!(Text.Length == 1 && Text == "-")) {
if (Hexadecimal) {
Value = Constrain(Convert.ToDecimal(Convert.ToInt32(Text, 16)));
}
else {
Value = Constrain(Decimal.Parse(Text, CultureInfo.CurrentCulture));
}
}
}
catch {
// Leave value as it is
}
finally {
UserEdit = false;
}
}
/// <devdoc>
/// Updates the index of the UpDownNumericAcceleration entry to use (if needed).
/// </devdoc>
private void SetNextAcceleration() {
// Spinning will check if accelerations is null.
if(this.Spinning && this.accelerationsCurrentIndex < (this.accelerations.Count - 1)) { // if index not the last entry ...
// Ticks are in 100-nanoseconds (1E-7 seconds).
long nowTicks = DateTime.Now.Ticks;
long buttonPressedElapsedTime = nowTicks - this.buttonPressedStartTime;
long accelerationInterval = 10000000L * this.accelerations[this.accelerationsCurrentIndex + 1].Seconds; // next entry.
// If Up/Down button pressed for more than the current acceleration entry interval, get next entry in the accel table.
if( buttonPressedElapsedTime > accelerationInterval )
{
this.buttonPressedStartTime = nowTicks;
this.accelerationsCurrentIndex++;
}
}
}
private void ResetIncrement() {
Increment = DefaultIncrement;
}
private void ResetMaximum() {
Maximum = DefaultMaximum;
}
private void ResetMinimum() {
Minimum = DefaultMinimum;
}
private void ResetValue() {
Value = DefaultValue;
}
/// <devdoc>
/// <para>Indicates whether the <see cref='System.Windows.Forms.NumericUpDown.Increment'/> property should be
/// persisted.</para>
/// </devdoc>
private bool ShouldSerializeIncrement() {
return !Increment.Equals(NumericUpDown.DefaultIncrement);
}
/// <devdoc>
/// <para>Indicates whether the <see cref='System.Windows.Forms.NumericUpDown.Maximum'/> property should be persisted.</para>
/// </devdoc>
private bool ShouldSerializeMaximum() {
return !Maximum.Equals(NumericUpDown.DefaultMaximum);
}
/// <devdoc>
/// <para>Indicates whether the <see cref='System.Windows.Forms.NumericUpDown.Minimum'/> property should be persisted.</para>
/// </devdoc>
private bool ShouldSerializeMinimum() {
return !Minimum.Equals(NumericUpDown.DefaultMinimum);
}
/// <devdoc>
/// <para>Indicates whether the <see cref='System.Windows.Forms.NumericUpDown.Value'/> property should be persisted.</para>
/// </devdoc>
private bool ShouldSerializeValue() {
return !Value.Equals(NumericUpDown.DefaultValue);
}
/// <devdoc>
/// Records when UpDownButtons are pressed to enable acceleration.
/// </devdoc>
private void StartAcceleration() {
this.buttonPressedStartTime = DateTime.Now.Ticks;
}
/// <devdoc>
/// Reset when UpDownButtons are pressed.
/// </devdoc>
private void StopAcceleration() {
this.accelerationsCurrentIndex = InvalidValue;
this.buttonPressedStartTime = InvalidValue;
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.ToString"]/*' />
/// <devdoc>
/// Provides some interesting info about this control in String form.
/// </devdoc>
/// <internalonly/>
public override string ToString() {
string s = base.ToString();
s += ", Minimum = " + Minimum.ToString(CultureInfo.CurrentCulture) + ", Maximum = " + Maximum.ToString(CultureInfo.CurrentCulture);
return s;
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.UpButton"]/*' />
/// <devdoc>
/// <para>
/// Increments the value of the up-down control.
/// </para>
/// </devdoc>
public override void UpButton() {
SetNextAcceleration();
if (UserEdit) {
ParseEditText();
}
Decimal newValue = currentValue;
// Operations on Decimals can throw OverflowException.
//
try{
newValue += this.Increment;
if (newValue > maximum){
newValue = maximum;
if( this.Spinning){
StopAcceleration();
}
}
}
catch( OverflowException ){
newValue = maximum;
}
Value = newValue;
}
private string GetNumberText(decimal num) {
string text;
if (Hexadecimal) {
text = ((Int64)num).ToString("X", CultureInfo.InvariantCulture);
Debug.Assert(text == text.ToUpper(CultureInfo.InvariantCulture), "GetPreferredSize assumes hex digits to be uppercase.");
}
else {
text = num.ToString((ThousandsSeparator ? "N" : "F") + DecimalPlaces.ToString(CultureInfo.CurrentCulture), CultureInfo.CurrentCulture);
}
return text;
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.UpdateEditText"]/*' />
/// <devdoc>
/// <para>
/// Displays the current value of the up-down control in the appropriate format.
/// </para>
/// </devdoc>
protected override void UpdateEditText() {
// If we're initializing, we don't want to update the edit text yet,
// just in case the value is invalid.
if (initializing) {
return;
}
// If the current value is user-edited, then parse this value before reformatting
if (UserEdit) {
ParseEditText();
}
// VSWhidbey 173332: Verify that the user is not starting the string with a "-"
// before attempting to set the Value property since a "-" is a valid character with
// which to start a string representing a negative number.
if (currentValueChanged || (!string.IsNullOrEmpty(Text) &&
!(Text.Length == 1 && Text == "-"))) {
currentValueChanged = false;
ChangingText = true;
// Make sure the current value is within the min/max
Debug.Assert(minimum <= currentValue && currentValue <= maximum,
"DecimalValue lies outside of [minimum, maximum]");
Text = GetNumberText(currentValue);
Debug.Assert(ChangingText == false, "ChangingText should have been set to false");
}
}
/// <include file='doc\NumericUpDown.uex' path='docs/doc[@for="NumericUpDown.ValidateEditText"]/*' />
/// <devdoc>
/// <para>
/// Validates and updates
/// the text displayed in the up-down control.
/// </para>
/// </devdoc>
protected override void ValidateEditText() {
// See if the edit text parses to a valid decimal
ParseEditText();
UpdateEditText();
}
// This is not a breaking change -- Even though this control previously autosized to hieght,
// it didn't actually have an AutoSize property. The new AutoSize property enables the
// smarter behavior.
internal override Size GetPreferredSizeCore(Size proposedConstraints) {
int height = PreferredHeight;
int baseSize = Hexadecimal ? 16 : 10;
int digit = GetLargestDigit(0, baseSize);
// The floor of log is intentionally 1 less than the number of digits. We initialize
// testNumber to account for the missing digit.
int numDigits = (int)Math.Floor(Math.Log(Math.Max(-(double)Minimum, (double)Maximum), baseSize));
int maxDigits;
if (this.Hexadecimal) {
maxDigits = (int)Math.Floor(Math.Log(Int64.MaxValue, baseSize));
}
else {
maxDigits = (int)Math.Floor(Math.Log((double)decimal.MaxValue, baseSize));
}
bool maxDigitsReached = numDigits >= maxDigits;
decimal testNumber;
// preinitialize testNumber with the leading digit
if(digit != 0 || numDigits == 1) {
testNumber = digit;
} else {
// zero can not be the leading digit if we need more than
// one digit. (0*baseSize = 0 in the loop below)
testNumber = GetLargestDigit(1, baseSize);
}
if (maxDigitsReached) {
// Prevent bug VSWhidbey 555288.
// decimal.MaxValue is 79228162514264337593543950335
// but 99999999999999999999999999999 is not a valid value for testNumber.
numDigits = maxDigits - 1;
}
// e.g., if the lagest digit is 7, and we can have 3 digits, the widest string would be "777"
for(int i = 0; i < numDigits; i++) {
testNumber = testNumber * baseSize + digit;
}
int textWidth = TextRenderer.MeasureText(GetNumberText(testNumber), this.Font).Width;
if (maxDigitsReached) {
string shortText;
if (this.Hexadecimal) {
shortText = ((Int64) testNumber).ToString("X", CultureInfo.InvariantCulture);
}
else {
shortText = testNumber.ToString(CultureInfo.CurrentCulture);
}
int shortTextWidth = TextRenderer.MeasureText(shortText, this.Font).Width;
// Adding the width of the one digit that was dropped earlier.
// This assumes that no additional thousand separator is added by that digit which is correct.
textWidth += shortTextWidth / (numDigits+1);
}
// Call AdjuctWindowRect to add space for the borders
int width = SizeFromClientSize(textWidth, height).Width + upDownButtons.Width;
return new Size(width, height) + Padding.Size;
}
private int GetLargestDigit(int start, int end) {
int largestDigit = -1;
int digitWidth = -1;
for(int i = start; i < end; i++) {
char ch;
if(i < 10) {
ch = i.ToString(CultureInfo.InvariantCulture)[0];
} else {
ch = (char)('A' + (i - 10));
}
Size digitSize = TextRenderer.MeasureText(ch.ToString(), this.Font);
if(digitSize.Width >= digitWidth) {
digitWidth = digitSize.Width;
largestDigit = i;
}
}
Debug.Assert(largestDigit != -1 && digitWidth != -1, "Failed to find largest digit.");
return largestDigit;
}
[System.Runtime.InteropServices.ComVisible(true)]
internal class NumericUpDownAccessibleObject : ControlAccessibleObject {
public NumericUpDownAccessibleObject(NumericUpDown owner) : base(owner) {
}
/// <summary>
/// Gets or sets the accessible name.
/// </summary>
public override string Name {
get {
string baseName = base.Name;
return ((NumericUpDown)Owner).GetAccessibleName(baseName);
}
set {
base.Name = value;
}
}
public override AccessibleRole Role {
get {
AccessibleRole role = Owner.AccessibleRole;
if (role != AccessibleRole.Default) {
return role;
}
else {
if (AccessibilityImprovements.Level1) {
return AccessibleRole.SpinButton;
}
else {
return AccessibleRole.ComboBox;
}
}
}
}
public override AccessibleObject GetChild(int index) {
if (index >= 0 && index < GetChildCount()) {
// TextBox child
//
if (index == 0) {
return ((UpDownBase)Owner).TextBox.AccessibilityObject.Parent;
}
// Up/down buttons
//
if (index == 1) {
return ((UpDownBase)Owner).UpDownButtonsInternal.AccessibilityObject.Parent;
}
}
return null;
}
public override int GetChildCount() {
return 2;
}
}
}
}
|