File: winforms\Managed\System\WinForms\UpDownBase.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="UpDownBase.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms {
    using System.Runtime.Serialization.Formatters;
    using System.Threading;
    using System.Runtime.Remoting;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Diagnostics;
    using System;
    using System.Security.Permissions;
    using System.Windows.Forms.VisualStyles;
    using System.Drawing;
    using Microsoft.Win32;
 
    /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase"]/*' />
    /// <devdoc>
    ///    <para>Implements the basic
    ///       functionality required by an up-down control.</para>
    /// </devdoc>
    [
    ComVisible(true),
    ClassInterface(ClassInterfaceType.AutoDispatch),
    Designer("System.Windows.Forms.Design.UpDownBaseDesigner, " + AssemblyRef.SystemDesign),
    System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors") // Shipped in Everett
    ]
    public abstract class UpDownBase : ContainerControl {
 
        private const int                       DefaultWheelScrollLinesPerPage = 1;
        private const int                       DefaultButtonsWidth = 16;
        private const int                       DefaultControlWidth = 120;
        private const int                       ThemedBorderWidth = 1; // width of custom border we draw when themed
        private const BorderStyle               DefaultBorderStyle = BorderStyle.Fixed3D;
        private static readonly bool            DefaultInterceptArrowKeys = true;
        private const LeftRightAlignment        DefaultUpDownAlign = LeftRightAlignment.Right;
        private const int                       DefaultTimerInterval = 500;
 
        ////////////////////////////////////////////////////////////////////////
        // Member variables
        //
        ////////////////////////////////////////////////////////////////////////
 
        // Child controls
        internal UpDownEdit upDownEdit; // See nested class at end of this file
        internal UpDownButtons upDownButtons; // See nested class at end of this file
 
        // Intercept arrow keys?
        private bool interceptArrowKeys = DefaultInterceptArrowKeys;
 
        // If true, the updown buttons will be drawn on the left-hand side of the control.
        private LeftRightAlignment upDownAlign = DefaultUpDownAlign;
 
        // userEdit is true when the text of the control has been changed,
        // and the internal value of the control has not yet been updated.
        // We do not always want to keep the internal value up-to-date,
        // hence this variable.
        private bool userEdit = false;
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.borderStyle"]/*' />
        /// <devdoc>
        ///     The current border for this edit control.
        /// </devdoc>
        private BorderStyle borderStyle = DefaultBorderStyle;
 
        // Mouse wheel movement
        private int wheelDelta = 0;
 
        // Indicates if the edit text is being changed
        private bool changingText = false;
 
        // Indicates whether we have doubleClicked
        private bool doubleClickFired = false;
 
        internal int defaultButtonsWidth = DefaultButtonsWidth;
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownBase"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Windows.Forms.UpDownBase'/>
        ///       class.
        ///    </para>
        /// </devdoc>
        public UpDownBase() {
            if (DpiHelper.IsScalingRequired) {
                defaultButtonsWidth = LogicalToDeviceUnits(DefaultButtonsWidth);
            }
 
            upDownButtons = new UpDownButtons(this);
            upDownEdit = new UpDownEdit(this);
            upDownEdit.BorderStyle = BorderStyle.None;
            upDownEdit.AutoSize = false;
            upDownEdit.KeyDown += new KeyEventHandler(this.OnTextBoxKeyDown);
            upDownEdit.KeyPress += new KeyPressEventHandler(this.OnTextBoxKeyPress);
            upDownEdit.TextChanged += new EventHandler(this.OnTextBoxTextChanged);
            upDownEdit.LostFocus += new EventHandler(this.OnTextBoxLostFocus);
            upDownEdit.Resize += new EventHandler(this.OnTextBoxResize);
            upDownButtons.TabStop = false;
            upDownButtons.Size = new Size(defaultButtonsWidth, PreferredHeight);
            upDownButtons.UpDown += new UpDownEventHandler(this.OnUpDown);
 
            Controls.AddRange(new Control[] { upDownButtons, upDownEdit} );
 
            SetStyle(ControlStyles.Opaque | ControlStyles.FixedHeight | ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.StandardClick, false);
            SetStyle(ControlStyles.UseTextForAccessibility, false);
        }
 
        ////////////////////////////////////////////////////////////////////////
        // Properties
        //
        ////////////////////////////////////////////////////////////////////////
 
        // AutoScroll is not relevant to an UpDownBase
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.AutoScroll"]/*' />
        /// <hideinheritance/>
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override bool AutoScroll {
            get {
                return false;
            }
            set {
                // Don't allow AutoScroll to be set to anything
            }
        }
 
        // AutoScrollMargin is not relevant to an UpDownBase
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.AutoScrollMargin"]/*' />
        /// <internalonly/>
        /// <hideinheritance/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        new public Size AutoScrollMargin {
            get {
                return base.AutoScrollMargin;
            }
            set {
                base.AutoScrollMargin = value;
            }
        }
 
        // AutoScrollMinSize is not relevant to an UpDownBase
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.AutoScrollMinSize"]/*' />
        /// <internalonly/>
        /// <hideinheritance/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        new public Size AutoScrollMinSize {
            get {
                return base.AutoScrollMinSize;
            }
            set {
                base.AutoScrollMinSize = value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.AutoSize"]/*' />
        /// <devdoc>
        ///    <para> Override to re-expose AutoSize.</para>
        /// </devdoc>
        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public override bool AutoSize
        {
            get
            {
                return base.AutoSize;
            }
            set
            {
                base.AutoSize = value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.AutoSizeChanged"]/*' />
        [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)]
        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
        new public event EventHandler AutoSizeChanged
        {
            add
            {
                base.AutoSizeChanged += value;
            }
            remove
            {
                base.AutoSizeChanged -= value;
            }
        }
        
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.BackColor"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the background color for the
        ///       text box portion of the up-down control.
        ///    </para>
        /// </devdoc>
        public override Color BackColor {
            get {
                return upDownEdit.BackColor;
            }
            set {
                base.BackColor = value; // Don't remove this or you will break serialization. See VSWhidbey #517574
                upDownEdit.BackColor = value;
                Invalidate(); // VSWhidbey #335074
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.BackgroundImage"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override Image BackgroundImage {
            get {
                return base.BackgroundImage;
            }
            set {
                base.BackgroundImage = value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.BackgroundImageChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler BackgroundImageChanged {
            add {
                base.BackgroundImageChanged += value;
            }
            remove {
                base.BackgroundImageChanged -= value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.BackgroundImageLayout"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override ImageLayout BackgroundImageLayout {
            get {
                return base.BackgroundImageLayout;
            }
            set {
                base.BackgroundImageLayout = value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.BackgroundImageLayoutChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler BackgroundImageLayoutChanged {
            add {
                base.BackgroundImageLayoutChanged += value;
            }
            remove {
                base.BackgroundImageLayoutChanged -= value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.BorderStyle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the border style for
        ///       the up-down control.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatAppearance),
        DefaultValue(BorderStyle.Fixed3D),
        DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE),
        SRDescription(SR.UpDownBaseBorderStyleDescr)
        ]
        public BorderStyle BorderStyle {
            get {
                return borderStyle;
            }
 
            set {
                //valid values are 0x0 to 0x2
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D))
                {
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle));
                }
 
                if (borderStyle != value) {
                    borderStyle = value;
                    RecreateHandle();
                }
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.ChangingText"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the text
        ///       property is being changed internally by its parent class.
        ///    </para>
        /// </devdoc>
        protected bool ChangingText {
            get {
                return changingText;
            }
 
            set {
                changingText = value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.ContextMenu"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        public override ContextMenu ContextMenu {
            get {
                return base.ContextMenu;
            }
            set {
                base.ContextMenu = value;
                this.upDownEdit.ContextMenu = value;
            }
        }
 
        public override ContextMenuStrip ContextMenuStrip {
            get {
                return base.ContextMenuStrip;
            }
            set {
                base.ContextMenuStrip = value;
                this.upDownEdit.ContextMenuStrip = value;
            }
        }
 
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.CreateParams"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Returns the parameters needed to create the handle. Inheriting classes
        ///       can override this to provide extra functionality. They should not,
        ///       however, forget to call base.getCreateParams() first to get the struct
        ///       filled up with the basic info.
        ///    </para>
        /// </devdoc>
        protected override CreateParams CreateParams {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
            get {
                CreateParams cp = base.CreateParams;
 
                cp.Style &= (~NativeMethods.WS_BORDER);
                if (!Application.RenderWithVisualStyles) {
                    switch (borderStyle) {
                        case BorderStyle.Fixed3D:
                            cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE;
                            break;
                        case BorderStyle.FixedSingle:
                            cp.Style |= NativeMethods.WS_BORDER;
                            break;
                    }
                }
                return cp;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.DefaultSize"]/*' />
        /// <devdoc>
        ///     Deriving classes can override this to configure a default size for their control.
        ///     This is more efficient than setting the size in the control's constructor.
        /// </devdoc>
        protected override Size DefaultSize {
            get {
                return new Size(DefaultControlWidth, PreferredHeight);
            }
        }
 
        // DockPadding is not relevant to UpDownBase
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.DockPadding"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        new public DockPaddingEdges DockPadding {
            get {
                return base.DockPadding;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.Focused"]/*' />
        /// <devdoc>
        ///     Returns true if this control has focus.
        /// </devdoc>
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        SRDescription(SR.ControlFocusedDescr)
        ]
        public override bool Focused {
            get {
                return upDownEdit.Focused;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.ForeColor"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Indicates the foreground color for the control.
        ///    </para>
        /// </devdoc>
        public override Color ForeColor {
            get {
                return upDownEdit.ForeColor;
            }
            set {
                base.ForeColor = value;
                upDownEdit.ForeColor = value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.InterceptArrowKeys"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether
        ///       the user can use the UP
        ///       ARROW and DOWN ARROW keys to select values.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(true),
        SRDescription(SR.UpDownBaseInterceptArrowKeysDescr)
        ]
        public bool InterceptArrowKeys {
 
            get {
                return interceptArrowKeys;
            }
 
            set {
                interceptArrowKeys = value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.MaximumSize"]/*' />
        public override Size MaximumSize {
            get { return base.MaximumSize; }
            set {
                base.MaximumSize = new Size(value.Width, 0);
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.MinimumSize"]/*' />
        public override Size MinimumSize {
            get { return base.MinimumSize; }
            set {
                base.MinimumSize = new Size(value.Width, 0);
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.MouseEnter"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler MouseEnter {
            add {
                base.MouseEnter += value;
            }
            remove {
                base.MouseEnter -= value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.MouseLeave"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler MouseLeave {
            add {
                base.MouseLeave += value;
            }
            remove {
                base.MouseLeave -= value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.MouseHover"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler MouseHover {
            add {
                base.MouseHover += value;
            }
            remove {
                base.MouseHover -= value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.MouseMove"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event MouseEventHandler MouseMove {
            add {
                base.MouseMove += value;
            }
            remove {
                base.MouseMove -= value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.PreferredHeight"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the height of
        ///       the up-down control.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatLayout),
        Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        SRDescription(SR.UpDownBasePreferredHeightDescr)
        ]
        public int PreferredHeight {
            get {
 
                int height = FontHeight;
 
                // Adjust for the border style
                if (borderStyle != BorderStyle.None) {
                    height += SystemInformation.BorderSize.Height * 4 + 3;
                }
                else {
                    height += 3;
                }
 
                return height;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.ReadOnly"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets
        ///       a
        ///       value
        ///       indicating whether the text may only be changed by the
        ///       use
        ///       of the up or down buttons.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(false),
        SRDescription(SR.UpDownBaseReadOnlyDescr)
        ]
        public bool ReadOnly {
 
            get {
                return upDownEdit.ReadOnly;
            }
 
            set {
                upDownEdit.ReadOnly = value;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.Text"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the text
        ///       displayed in the up-down control.
        ///    </para>
        /// </devdoc>
        [
        Localizable(true)
        ]
        public override string Text {
            get {
                return upDownEdit.Text;
            }
 
            set {
                upDownEdit.Text = value;
                // The text changed event will at this point be triggered.
                // After returning, the value of UserEdit will reflect
                // whether or not the current upDownEditbox text is in sync
                // with any internally stored values. If UserEdit is true,
                // we must validate the text the user typed or set.
 
                ChangingText = false;
                // Details: Usually, the code in the Text changed event handler
                // sets ChangingText back to false.
                // If the text hasn't actually changed though, the event handler
                // never fires. ChangingText should always be false on exit from
                // this property.
 
                if (UserEdit) {
                    ValidateEditText();
                }
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.TextAlign"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or
        ///       sets the alignment of the text in the up-down
        ///       control.
        ///    </para>
        /// </devdoc>
        [
        Localizable(true),
        SRCategory(SR.CatAppearance),
        DefaultValue(HorizontalAlignment.Left),
        SRDescription(SR.UpDownBaseTextAlignDescr)
        ]
        public HorizontalAlignment TextAlign {
            get {
                return upDownEdit.TextAlign;
            }
            set {
                //valid values are 0x0 to 0x2
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center))
                {
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment));
                }
                upDownEdit.TextAlign = value;
            }
        }
 
        internal TextBox TextBox {
            get {
                return upDownEdit;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownAlign"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets
        ///       or sets the
        ///       alignment
        ///       of the up and down buttons on the up-down control.
        ///    </para>
        /// </devdoc>
        [
        Localizable(true),
        SRCategory(SR.CatAppearance),
        DefaultValue(LeftRightAlignment.Right),
        SRDescription(SR.UpDownBaseAlignmentDescr)
        ]
        public LeftRightAlignment UpDownAlign {
 
            get {
                return upDownAlign;
            }
 
            set {
                //valid values are 0x0 to 0x1
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)LeftRightAlignment.Left, (int)LeftRightAlignment.Right))
                {
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(LeftRightAlignment));
                }
 
                if (upDownAlign != value) {
 
                    upDownAlign = value;
                    PositionControls();
                    Invalidate();
                }
            }
        }
 
        internal UpDownButtons UpDownButtonsInternal {
            get {
                return upDownButtons;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UserEdit"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets
        ///       or sets a value indicating whether a value has been entered by the
        ///       user.
        ///    </para>
        /// </devdoc>
        protected bool UserEdit {
            get {
                return userEdit;
            }
 
            set {
                userEdit = value;
            }
        }
 
 
        ////////////////////////////////////////////////////////////////////////
        // Methods
        //
        ////////////////////////////////////////////////////////////////////////
       
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.DownButton"]/*' />
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class, handles the pressing of the down button
        ///       on the up-down control.
        ///    </para>
        /// </devdoc>
        public abstract void DownButton();
 
        // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose
        // constraints on their size.
        internal override Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) {
            return base.ApplyBoundsConstraints(suggestedX,suggestedY, proposedWidth, PreferredHeight);
        }
 
        /// <summary>
        /// Gets an accessible name.
        /// </summary>
        /// <param name="baseName">The base name.</param>
        /// <returns>The accessible name.</returns>
        internal string GetAccessibleName(string baseName) {
            if (baseName == null) {
                if (AccessibilityImprovements.Level3) {
                    return SR.GetString(SR.SpinnerAccessibleName);
                }
                else if (AccessibilityImprovements.Level1) {
                    return this.GetType().Name;
                }
            }
 
            return baseName;
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.RescaleConstantsForDpi"]/*' />
        /// <devdoc>
        ///       When overridden in a derived class, handles rescaling of any magic numbers used in control painting.
        ///       For UpDown controls, scale the width of the up/down buttons.
        ///       Must call the base class method to get the current DPI values. This method is invoked only when 
        ///       Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has 
        ///       EnableDpiChangedMessageHandling config switch turned on.
        /// </devdoc>
        protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) {
            base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew);
            defaultButtonsWidth = LogicalToDeviceUnits(DefaultButtonsWidth);
            upDownButtons.Width = defaultButtonsWidth;
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnChanged"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// <para>When overridden in a derived class, raises the Changed event.
        /// event.</para>
        /// </devdoc>
        protected virtual void OnChanged(object source, EventArgs e) {
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnHandleCreated"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Initialize the updown. Adds the upDownEdit and updown buttons.
        ///    </para>
        /// </devdoc>
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            PositionControls();
            SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.UserPreferenceChanged);
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnHandleCreated"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Tear down the updown.
        ///    </para>
        /// </devdoc>
        protected override void OnHandleDestroyed(EventArgs e)
        {
            SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.UserPreferenceChanged);
            base.OnHandleDestroyed(e);
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnPaint"]/*' />
        /// <devdoc>
        ///     Handles painting the buttons on the control.
        ///
        /// </devdoc>
        protected override void OnPaint(PaintEventArgs e) {
            base.OnPaint(e);
            
            Rectangle editBounds = upDownEdit.Bounds;
            if (Application.RenderWithVisualStyles) {
                if (borderStyle != BorderStyle.None) {
                    Rectangle bounds = ClientRectangle;
                    Rectangle clipBounds = e.ClipRectangle;
 
                    //Draw a themed textbox-like border, which is what the spin control does
                    VisualStyleRenderer vsr = new VisualStyleRenderer(VisualStyleElement.TextBox.TextEdit.Normal);
                    int border = ThemedBorderWidth;
                    Rectangle clipLeft = new Rectangle(bounds.Left, bounds.Top, border, bounds.Height);
                    Rectangle clipTop = new Rectangle(bounds.Left, bounds.Top, bounds.Width, border);
                    Rectangle clipRight = new Rectangle(bounds.Right - border, bounds.Top, border, bounds.Height);
                    Rectangle clipBottom = new Rectangle(bounds.Left, bounds.Bottom - border, bounds.Width, border);
                    clipLeft.Intersect(clipBounds);
                    clipTop.Intersect(clipBounds);
                    clipRight.Intersect(clipBounds);
                    clipBottom.Intersect(clipBounds);
                    vsr.DrawBackground(e.Graphics, bounds, clipLeft, HandleInternal);
                    vsr.DrawBackground(e.Graphics, bounds, clipTop, HandleInternal);
                    vsr.DrawBackground(e.Graphics, bounds, clipRight, HandleInternal);
                    vsr.DrawBackground(e.Graphics, bounds, clipBottom, HandleInternal);
                    // Draw rectangle around edit control with background color
                    using (Pen pen = new Pen(BackColor)) {
                        Rectangle backRect = editBounds;
                        backRect.X--;
                        backRect.Y--;
                        backRect.Width++;
                        backRect.Height++;
                        e.Graphics.DrawRectangle(pen, backRect);
                    }
                }
            }
            else {
                // Draw rectangle around edit control with background color
                using (Pen pen = new Pen(BackColor, Enabled ? 2 : 1))
                {
                    Rectangle backRect = editBounds;
                    backRect.Inflate(1, 1);
                    if (!Enabled)
                    {
                        backRect.X--;
                        backRect.Y--;
                        backRect.Width++;
                        backRect.Height++;
                    }
                    e.Graphics.DrawRectangle(pen, backRect);
                }
            }
            if (!Enabled && BorderStyle != BorderStyle.None && !upDownEdit.ShouldSerializeBackColor()) {
                //draws a grayed rectangled around the upDownEdit, since otherwise we will have a white
                //border around the upDownEdit, which is inconsistent with Windows' behavior
                //we only want to do this when BackColor is not serialized, since otherwise
                //we should display the backcolor instead of the usual grayed textbox.
                editBounds.Inflate(1, 1);
                ControlPaint.DrawBorder(e.Graphics, editBounds, SystemColors.Control, ButtonBorderStyle.Solid);
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnTextBoxKeyDown"]/*' />
        /// <devdoc>
        /// <para>Raises the <see cref='System.Windows.Forms.Control.KeyDown'/>
        /// event.</para>
        /// </devdoc>
        protected virtual void OnTextBoxKeyDown(object source, KeyEventArgs e) {
            this.OnKeyDown(e);
            if (interceptArrowKeys) {
 
                // Intercept up arrow
                if (e.KeyData == Keys.Up) {
                    UpButton();
                    e.Handled = true;
                }
 
                // Intercept down arrow
                else if (e.KeyData == Keys.Down) {
                    DownButton();
                    e.Handled = true;
                }
            }
 
            // Perform text validation if ENTER is pressed
            //
            if (e.KeyCode == Keys.Return && UserEdit) {
                ValidateEditText();
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnTextBoxKeyPress"]/*' />
        /// <devdoc>
        /// <para>Raises the <see cref='System.Windows.Forms.Control.KeyPress'/>
        /// event.</para>
        /// </devdoc>
        protected virtual void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
            this.OnKeyPress(e);
 
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnTextBoxLostFocus"]/*' />
        /// <devdoc>
        /// <para>Raises the <see cref='System.Windows.Forms.Control.LostFocus'/> event.</para>
        /// </devdoc>
        protected virtual void OnTextBoxLostFocus(object source, EventArgs e) {
            if (UserEdit) {
                ValidateEditText();
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnTextBoxResize"]/*' />
        /// <devdoc>
        /// <para>Raises the <see cref='System.Windows.Forms.Control.Resize'/> event.</para>
        /// </devdoc>
        protected virtual void OnTextBoxResize(object source, EventArgs e) {
            this.Height = PreferredHeight;
            PositionControls();
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnTextBoxTextChanged"]/*' />
        /// <devdoc>
        /// <para>Raises the TextBoxTextChanged event.
        /// event.</para>
        /// </devdoc>
        protected virtual void OnTextBoxTextChanged(object source, EventArgs e) {
            if (changingText) {
                Debug.Assert(UserEdit == false, "OnTextBoxTextChanged() - UserEdit == true");
                ChangingText = false;
            }
            else {
                UserEdit = true;
            }
 
            this.OnTextChanged(e);
            OnChanged(source, new EventArgs());
        }
 
        /// <devdoc>
        ///     Called from the UpDownButtons member. Provided for derived controls to have a finer way to handle the event.
        /// </devdoc>
        internal virtual void OnStartTimer() {
        }
 
        internal virtual void OnStopTimer() {
        }
 
        /// <devdoc>
        ///       Raises the <see cref='System.Windows.Forms.Control.OnMouseDown'/> event.
        /// </devdoc>
        protected override void OnMouseDown(MouseEventArgs e) {
            if (e.Clicks == 2 && e.Button == MouseButtons.Left) {
                doubleClickFired = true;
            }
 
            base.OnMouseDown(e);
        }
 
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Raises the <see cref='System.Windows.Forms.Control.OnMouseUp'/> event.
        ///
        ///    </para>
        /// </devdoc>
        protected override void OnMouseUp(MouseEventArgs mevent) {
            if (mevent.Button == MouseButtons.Left) {
                Point pt = PointToScreen(new Point(mevent.X, mevent.Y));
                if (UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle && !ValidationCancelled) {
                    if (!doubleClickFired) {
                        OnClick(mevent);
                        OnMouseClick(mevent);
                    }
                    else {
                        doubleClickFired = false;
                        OnDoubleClick(mevent);
                        OnMouseDoubleClick(mevent);
                    }
                }
                doubleClickFired = false;
            }
            base.OnMouseUp(mevent);
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnMouseWheel"]/*' />
        /// <devdoc>
        /// <para>Raises the <see cref='System.Windows.Forms.Control.OnMouseWheel'/> event.</para>
        /// </devdoc>
        protected override void OnMouseWheel(MouseEventArgs e) {
            base.OnMouseWheel(e);
            HandledMouseEventArgs hme = e as HandledMouseEventArgs;
            if (hme != null) {
               if (hme.Handled) {
                   return;
               }
               hme.Handled = true;
            }
 
            if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None) {
                return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down.
            }
 
            int wheelScrollLines = SystemInformation.MouseWheelScrollLines;
            if (wheelScrollLines == 0) {
                return; // Do not scroll when the user system setting is 0 lines per notch
            }
 
            Debug.Assert(this.wheelDelta > -NativeMethods.WHEEL_DELTA, "wheelDelta is too smal");
            Debug.Assert(this.wheelDelta < NativeMethods.WHEEL_DELTA, "wheelDelta is too big");
            this.wheelDelta += e.Delta;
 
            float partialNotches;
            partialNotches = (float)this.wheelDelta / (float)NativeMethods.WHEEL_DELTA;
 
            if (wheelScrollLines == -1) {
               wheelScrollLines = DefaultWheelScrollLinesPerPage;
            }
 
            // Evaluate number of bands to scroll
            int scrollBands = (int)((float)wheelScrollLines * partialNotches);
            if (scrollBands != 0) {
               int absScrollBands;
               if (scrollBands > 0) {
                  absScrollBands = scrollBands;
                  while (absScrollBands > 0) {
                     UpButton();
                     absScrollBands--;
                  }
                  this.wheelDelta -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines));
               }
               else {
                  absScrollBands = -scrollBands;
                  while (absScrollBands > 0) {
                     DownButton();
                     absScrollBands--;
                  }
                  this.wheelDelta -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines));
               }
            }
        }
 
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnLayout"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    Handle the layout event. The size of the upDownEdit control, and the
        ///    position of the UpDown control must be modified.
        /// </devdoc>
        protected override void OnLayout(LayoutEventArgs e) {
 
            PositionControls();
            base.OnLayout(e);
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnFontChanged"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Raises the FontChanged event.
        ///    </para>
        /// </devdoc>
        protected override void OnFontChanged(EventArgs e) {
            // Clear the font height cache
            FontHeight = -1;
 
            Height = PreferredHeight;
            PositionControls();
 
            base.OnFontChanged(e);
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.OnUpDown"]/*' />
        /// <devdoc>
        ///
        ///     Handles UpDown events, which are generated by clicking on
        ///     the updown buttons in the child updown control.
        ///
        /// </devdoc>
        private void OnUpDown(object source, UpDownEventArgs e) {
            // Modify the value
            if (e.ButtonID == (int)ButtonID.Up)
                UpButton();
            else if (e.ButtonID == (int)ButtonID.Down)
                DownButton();
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.PositionControls"]/*' />
        /// <devdoc>
        ///     Calculates the size and position of the upDownEdit control and
        ///     the updown buttons.
        /// </devdoc>
        private void PositionControls() {
            Rectangle upDownEditBounds    = Rectangle.Empty,
                      upDownButtonsBounds = Rectangle.Empty;
 
            Rectangle clientArea     = new Rectangle(Point.Empty, ClientSize);
            int totalClientWidth     = clientArea.Width;
            bool themed              = Application.RenderWithVisualStyles;
            BorderStyle borderStyle  = BorderStyle;
 
 
            // determine how much to squish in - Fixed3d and FixedSingle have 2PX border
            int borderWidth = (borderStyle == BorderStyle.None) ? 0 : 2;
            clientArea.Inflate(-borderWidth, -borderWidth);
 
            // Reposition and resize the upDownEdit control
            //
            if (upDownEdit != null) {
                upDownEditBounds = clientArea;
                upDownEditBounds.Size = new Size(clientArea.Width - defaultButtonsWidth, clientArea.Height);
            }
 
            // Reposition and resize the updown buttons
            //
            if (upDownButtons != null) {
                int borderFixup = (themed) ? 1: 2;
                if (borderStyle == BorderStyle.None) {
                    borderFixup = 0;
                }
                upDownButtonsBounds = new Rectangle(/*x*/clientArea.Right - defaultButtonsWidth+borderFixup,
                                                    /*y*/clientArea.Top-borderFixup,
                                                    /*w*/defaultButtonsWidth,
                                                    /*h*/clientArea.Height+(borderFixup*2));
            }
 
            // Right to left translation
            LeftRightAlignment updownAlign = UpDownAlign;
            updownAlign = RtlTranslateLeftRight(updownAlign);
 
            // left/right updown align translation
            if (updownAlign == LeftRightAlignment.Left) {
                // if the buttons are aligned to the left, swap position of text box/buttons
                upDownButtonsBounds.X = totalClientWidth - upDownButtonsBounds.Right;
                upDownEditBounds.X = totalClientWidth - upDownEditBounds.Right;
            }
 
            // apply locations
            if (upDownEdit != null) {
                 upDownEdit.Bounds = upDownEditBounds;
            }
            if (upDownButtons != null) {
                upDownButtons.Bounds = upDownButtonsBounds;
                upDownButtons.Invalidate();
            }
 
       }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.Select"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Selects a range of
        ///       text in the up-down control.
        ///    </para>
        /// </devdoc>
        public void Select(int start, int length) {
            upDownEdit.Select(start, length);
        }
 
 
        /// <devdoc>
        ///   Child controls run their
        /// </devdoc>
        private MouseEventArgs TranslateMouseEvent(Control child, MouseEventArgs e) {
            if (child != null && IsHandleCreated) {
                // same control as PointToClient or PointToScreen, just
                // with two specific controls in mind.
                NativeMethods.POINT point = new NativeMethods.POINT(e.X, e.Y);
                UnsafeNativeMethods.MapWindowPoints(new HandleRef(child, child.Handle), new HandleRef(this, Handle), point, 1);
                return new MouseEventArgs(e.Button, e.Clicks, point.x, point.y , e.Delta);
            }
            return e;
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpButton"]/*' />
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class, handles the pressing of the up button on the up-down control.
        ///    </para>
        /// </devdoc>
        public abstract void UpButton();
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpdateEditText"]/*' />
        /// <devdoc>
        ///    <para>
        ///       When overridden
        ///       in a derived class, updates the text displayed in the up-down control.
        ///    </para>
        /// </devdoc>
        protected abstract void UpdateEditText();
        
        private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) {
            if (pref.Category == UserPreferenceCategory.Locale) {
                UpdateEditText();
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.ValidateEditText"]/*' />
        /// <devdoc>
        ///    <para>
        ///       When overridden in a
        ///       derived class, validates the text displayed in the up-down control.
        ///    </para>
        /// </devdoc>
        protected virtual void ValidateEditText() {
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.WndProc"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case NativeMethods.WM_SETFOCUS:
                    if (!HostedInWin32DialogManager) {
                        if (ActiveControl == null) {
                            SetActiveControlInternal(TextBox);
                        }
                        else {
                            FocusActiveControlInternal();
                        }
                    }
                    else {
                        if (TextBox.CanFocus){
                            UnsafeNativeMethods.SetFocus(new HandleRef(TextBox, TextBox.Handle));
                        }
                        base.WndProc(ref m);
                    }
                    break;
                case NativeMethods.WM_KILLFOCUS:
                    DefWndProc(ref m);
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.SetToolTip"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    This Function sets the ToolTip for this composite control.
        /// </devdoc>
        internal void SetToolTip(ToolTip toolTip, string caption) {
            toolTip.SetToolTip(this.upDownEdit , caption);
            toolTip.SetToolTip(this.upDownButtons , caption);
        }
 
        internal class UpDownEdit : TextBox{
            /////////////////////////////////////////////////////////////////////
            // Member variables
            //
            /////////////////////////////////////////////////////////////////////
 
            // Parent control
            private UpDownBase parent;
            private bool doubleClickFired = false;
            /////////////////////////////////////////////////////////////////////
            // Constructors
            //
            /////////////////////////////////////////////////////////////////////
 
            internal UpDownEdit(UpDownBase parent)
            : base() {
 
                SetStyle(ControlStyles.FixedHeight |
                         ControlStyles.FixedWidth, true);
 
                SetStyle(ControlStyles.Selectable, false);
 
                this.parent = parent;
            }
 
            public override string Text {
                get {
                    return base.Text;
                }
                set {
                    bool valueChanged = (value != base.Text);
                    base.Text = value;      
                    if (valueChanged && AccessibilityImprovements.Level1) {
                            AccessibilityNotifyClients(AccessibleEvents.NameChange, -1);
                    }
                }
            }
 
            protected override AccessibleObject CreateAccessibilityInstance() {
                return new UpDownEditAccessibleObject(this, parent);
            }
 
            protected override void OnMouseDown(MouseEventArgs e) {
                if (e.Clicks == 2 && e.Button == MouseButtons.Left) {
                    doubleClickFired = true;
                }
                parent.OnMouseDown(parent.TranslateMouseEvent(this, e));
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownEdit.OnMouseUp"]/*' />
            /// <devdoc>
            ///
            ///     Handles detecting when the mouse button is released.
            ///
            /// </devdoc>
            protected override void OnMouseUp(MouseEventArgs e) {
 
                Point pt = new Point(e.X,e.Y);
                pt = PointToScreen(pt);
 
                MouseEventArgs me = parent.TranslateMouseEvent(this, e);
                if (e.Button == MouseButtons.Left) {
                    if (!parent.ValidationCancelled && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) {
                        if (!doubleClickFired) {
                            parent.OnClick(me);
                            parent.OnMouseClick(me);
                        }
                        else {
                            doubleClickFired = false;
                            parent.OnDoubleClick(me);
                            parent.OnMouseDoubleClick(me);
                        }
                    }
                    doubleClickFired = false;
                }
 
                parent.OnMouseUp(me);
            }
 
            internal override void WmContextMenu(ref Message m) {
                // VSWhidbey 521337: want to make the SourceControl to be the UpDownBase, not the Edit.
                if (ContextMenu == null && ContextMenuStrip != null) {
                    WmContextMenu(ref m, parent);
                }
                else {
                    WmContextMenu(ref m, this);
                }
            }
 
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownEdit.OnKeyUp"]/*' />
            /// <devdoc>
            /// <para>Raises the <see cref='System.Windows.Forms.Control.KeyUp'/>
            /// event.</para>
            /// </devdoc>
            protected override void OnKeyUp(KeyEventArgs e) {
                parent.OnKeyUp(e);
            }
 
            protected override void OnGotFocus(EventArgs e) {
                parent.SetActiveControlInternal(this);
                parent.InvokeGotFocus(parent, e);
            }
 
            protected override void OnLostFocus(EventArgs e) {
                parent.InvokeLostFocus(parent, e);
            }
 
            // Microsoft: Focus fixes. The XXXUpDown control will
            //         also fire a Leave event. We don't want
            //         to fire two of them.
            // protected override void OnLeave(EventArgs e) {
            //     parent.OnLeave(e);
            // }
 
            // Create our own accessibility object to map the accessible name
            // back to our parent.  They should track.
            internal class UpDownEditAccessibleObject : ControlAccessibleObject {
                UpDownBase parent;
 
                public UpDownEditAccessibleObject(UpDownEdit owner, UpDownBase parent) : base(owner) {
                    this.parent = parent;
                }
 
                public override string Name {
                    get {
                        return parent.AccessibilityObject.Name;
                    }
                    set {
                        parent.AccessibilityObject.Name = value;
                    }
                }
 
                public override string KeyboardShortcut {
                    get {
                        return parent.AccessibilityObject.KeyboardShortcut;
                    }
                }
            }
        }
 
        /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons"]/*' />
        /// <devdoc>
        ///
        ///     Nested class UpDownButtons
        ///
        ///     A control representing the pair of buttons on the end of the upDownEdit control.
        ///     This class handles drawing the updown buttons, and detecting mouse actions
        ///     on these buttons. Acceleration on the buttons is handled. The control
        ///     sends UpDownEventArgss to the parent UpDownBase class when a button is pressed,
        ///     or when the acceleration determines that another event should be generated.
        /// </devdoc>
        internal class UpDownButtons : Control {
            // 
 
            /////////////////////////////////////////////////////////////////////
            // Member variables
            //
            /////////////////////////////////////////////////////////////////////
 
            // Parent control
            private UpDownBase parent;
 
            // Button state
            private ButtonID pushed = ButtonID.None;
            private ButtonID captured = ButtonID.None;
            private ButtonID mouseOver = ButtonID.None;
 
            // UpDown event handler
            private UpDownEventHandler upDownEventHandler;
 
            // Timer
            private Timer timer;                    // generates UpDown events
            private int timerInterval;              // milliseconds between events
 
            private bool doubleClickFired = false;
 
            /////////////////////////////////////////////////////////////////////
            // Constructors
            //
            /////////////////////////////////////////////////////////////////////
 
            internal UpDownButtons(UpDownBase parent)
 
            : base() {
 
                SetStyle(ControlStyles.Opaque | ControlStyles.FixedHeight |
                         ControlStyles.FixedWidth, true);
 
                SetStyle(ControlStyles.Selectable, false);
 
                this.parent = parent;
            }
 
 
            /////////////////////////////////////////////////////////////////////
            // Methods
            //
            /////////////////////////////////////////////////////////////////////
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.UpDown"]/*' />
            /// <devdoc>
            ///
            ///     Adds a handler for the updown button event.
            /// </devdoc>
            public event UpDownEventHandler UpDown {
                add {
                    upDownEventHandler += value;
                }
                remove {
                    upDownEventHandler -= value;
                }
            }
 
            // Called when the mouse button is pressed - we need to start
            // spinning the value of the updown.
            //
            private void BeginButtonPress(MouseEventArgs e) {
 
                int half_height = Size.Height / 2;
 
                if (e.Y < half_height) {
 
                    // Up button
                    //
                    pushed = captured = ButtonID.Up;
                    Invalidate();
 
                }
                else {
 
                    // Down button
                    //
                    pushed = captured = ButtonID.Down;
                    Invalidate();
                }
 
                // Capture the mouse
                //
                CaptureInternal = true;
 
                // Generate UpDown event
                //
                OnUpDown(new UpDownEventArgs((int)pushed));
 
                // Start the timer for new updown events
                //
                StartTimer();
            }
 
            protected override AccessibleObject CreateAccessibilityInstance() {
                return new UpDownButtonsAccessibleObject(this);
            }
 
            // Called when the mouse button is released - we need to stop
            // spinning the value of the updown.
            //
            private void EndButtonPress() {
 
                pushed = ButtonID.None;
                captured = ButtonID.None;
 
                // Stop the timer
                StopTimer();
 
                // Release the mouse
                CaptureInternal = false;
 
                // Redraw the buttons
                Invalidate();
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.OnMouseDown"]/*' />
            /// <devdoc>
            ///
            ///     Handles detecting mouse hits on the buttons. This method
            ///     detects which button was hit (up or down), fires a
            ///     updown event, captures the mouse, and starts a timer
            ///     for repeated updown events.
            ///
            /// </devdoc>
            protected override void OnMouseDown(MouseEventArgs e) {
                // Begin spinning the value
                //
 
                // Focus the parent
                //
                this.parent.FocusInternal();
 
                if (!parent.ValidationCancelled && e.Button == MouseButtons.Left) {
                    BeginButtonPress(e);
                }
                if (e.Clicks == 2 && e.Button == MouseButtons.Left) {
                    doubleClickFired = true;
                }
                // At no stage should a button be pushed, and the mouse
                // not captured.
                //
                Debug.Assert(!(pushed != ButtonID.None && captured == ButtonID.None),
                             "Invalid button pushed/captured combination");
 
                parent.OnMouseDown(parent.TranslateMouseEvent(this, e));
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.OnMouseMove"]/*' />
            /// <devdoc>
            ///
            ///     Handles detecting mouse movement.
            ///
            /// </devdoc>
            protected override void OnMouseMove(MouseEventArgs e) {
 
                // If the mouse is captured by the buttons (i.e. an updown button
                // was pushed, and the mouse button has not yet been released),
                // determine the new state of the buttons depending on where
                // the mouse pointer has moved.
 
                if (Capture) {
 
                    // Determine button area
 
                    Rectangle rect = ClientRectangle;
                    rect.Height /= 2;
 
                    if (captured == ButtonID.Down) {
                        rect.Y += rect.Height;
                    }
 
                    // Test if the mouse has moved outside the button area
 
                    if (rect.Contains(e.X, e.Y)) {
 
                        // Inside button
                        // Repush the button if necessary
 
                        if (pushed != captured) {
 
                            // Restart the timer
                            StartTimer();
 
                            pushed = captured;
                            Invalidate();
                        }
 
                    }
                    else {
 
                        // Outside button
                        // Retain the capture, but pop the button up whilst
                        // the mouse remains outside the button and the
                        // mouse button remains pressed.
 
                        if (pushed != ButtonID.None) {
 
                            // Stop the timer for updown events
                            StopTimer();
 
                            pushed = ButtonID.None;
                            Invalidate();
                        }
                    }
                }
 
                //Logic for seeing which button is Hot if any
                Rectangle rectUp = ClientRectangle, rectDown = ClientRectangle;
                rectUp.Height /= 2;
                rectDown.Y += rectDown.Height / 2;
 
                //Check if the mouse is on the upper or lower button. Note that it could be in neither.
                if (rectUp.Contains(e.X, e.Y)) {
                    mouseOver = ButtonID.Up;
                    Invalidate();
                }
                else if (rectDown.Contains(e.X, e.Y)) {
                    mouseOver = ButtonID.Down;
                    Invalidate();
                }
 
                // At no stage should a button be pushed, and the mouse
                // not captured.
                Debug.Assert(!(pushed != ButtonID.None && captured == ButtonID.None),
                             "Invalid button pushed/captured combination");
 
                parent.OnMouseMove(parent.TranslateMouseEvent(this, e));
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.OnMouseUp"]/*' />
            /// <devdoc>
            ///
            ///     Handles detecting when the mouse button is released.
            ///
            /// </devdoc>
            protected override void OnMouseUp(MouseEventArgs e) {
 
                if (!parent.ValidationCancelled && e.Button == MouseButtons.Left) {
                    EndButtonPress();
                }
 
                // At no stage should a button be pushed, and the mouse
                // not captured.
                Debug.Assert(!(pushed != ButtonID.None && captured == ButtonID.None),
                             "Invalid button pushed/captured combination");
 
                Point pt = new Point(e.X,e.Y);
                pt = PointToScreen(pt);
 
                MouseEventArgs me = parent.TranslateMouseEvent(this, e);
                if (e.Button == MouseButtons.Left) {
                    if (!parent.ValidationCancelled && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) {
                        if (!doubleClickFired) {
                            this.parent.OnClick(me);
                        }
                        else {
                            doubleClickFired = false;
                            this.parent.OnDoubleClick(me);
                            this.parent.OnMouseDoubleClick(me);
                        }
                    }
                    doubleClickFired = false;
                }
 
                parent.OnMouseUp(me);
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.OnMouseLeave"]/*' />
            /// <devdoc>
            ///
            ///     Handles detecting when the mouse leaves.
            ///
            /// </devdoc>
            protected override void OnMouseLeave(EventArgs e) {
                mouseOver = ButtonID.None;
                Invalidate();
 
                parent.OnMouseLeave(e);
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.OnPaint"]/*' />
            /// <devdoc>
            ///     Handles painting the buttons on the control.
            ///
            /// </devdoc>
            protected override void OnPaint(PaintEventArgs e) {
                int half_height = ClientSize.Height / 2;
 
                /* Draw the up and down buttons */
 
                if (Application.RenderWithVisualStyles) {
                    VisualStyleRenderer vsr = new VisualStyleRenderer(mouseOver == ButtonID.Up ? VisualStyleElement.Spin.Up.Hot : VisualStyleElement.Spin.Up.Normal);
 
                    if (!Enabled) {
                        vsr.SetParameters(VisualStyleElement.Spin.Up.Disabled);
                    }
                    else if (pushed == ButtonID.Up) {
                        vsr.SetParameters(VisualStyleElement.Spin.Up.Pressed);
                    }
 
                    vsr.DrawBackground(e.Graphics, new Rectangle(0, 0, parent.defaultButtonsWidth, half_height), HandleInternal);
 
                    if (!Enabled) {
                        vsr.SetParameters(VisualStyleElement.Spin.Down.Disabled);
                    }
                    else if (pushed == ButtonID.Down) {
                        vsr.SetParameters(VisualStyleElement.Spin.Down.Pressed);
                    }
                    else {
                        vsr.SetParameters(mouseOver == ButtonID.Down ? VisualStyleElement.Spin.Down.Hot : VisualStyleElement.Spin.Down.Normal);
                    }
 
                    vsr.DrawBackground(e.Graphics, new Rectangle(0, half_height, parent.defaultButtonsWidth, half_height), HandleInternal);
                }
                else {
                    ControlPaint.DrawScrollButton(e.Graphics,
                                                  new Rectangle(0, 0, parent.defaultButtonsWidth, half_height),
                                                  ScrollButton.Up,
                                                  pushed == ButtonID.Up ? ButtonState.Pushed : (Enabled ? ButtonState.Normal : ButtonState.Inactive));
 
                    ControlPaint.DrawScrollButton(e.Graphics,
                                                  new Rectangle(0, half_height, parent.defaultButtonsWidth, half_height),
                                                  ScrollButton.Down,
                                                  pushed == ButtonID.Down ? ButtonState.Pushed : (Enabled ? ButtonState.Normal : ButtonState.Inactive));
                }
 
                if (half_height != (ClientSize.Height + 1) / 2) {
                    // When control has odd height, a line needs to be drawn below the buttons with the backcolor.
                    using (Pen pen = new Pen(this.parent.BackColor)) {
                        Rectangle clientRect = ClientRectangle;
                        e.Graphics.DrawLine(pen, clientRect.Left, clientRect.Bottom - 1, clientRect.Right - 1, clientRect.Bottom - 1);
                    }
                }
 
                base.OnPaint(e); // raise paint event, just in case this inner class goes public some day
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.OnUpDown"]/*' />
            /// <devdoc>
            ///     Occurs when the UpDown buttons are pressed and when the acceleration timer tick event is raised.
            /// </devdoc>
            protected virtual void OnUpDown(UpDownEventArgs upevent) {
                if (upDownEventHandler != null)
                    upDownEventHandler(this, upevent);
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.StartTimer"]/*' />
            /// <devdoc>
            ///     Starts the timer for generating updown events
            /// </devdoc>
            protected void StartTimer() {
                parent.OnStartTimer();
                if (timer == null) {
                    timer = new Timer();      // generates UpDown events
                    // Add the timer handler
                    timer.Tick += new EventHandler(TimerHandler);
                }
 
                this.timerInterval = DefaultTimerInterval;
 
                timer.Interval = this.timerInterval;
                timer.Start();
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.StopTimer"]/*' />
            /// <devdoc>
            ///     Stops the timer for generating updown events
            /// </devdoc>
            protected void StopTimer() {
                if (timer != null) {
                    timer.Stop();
                    timer.Dispose();
                    timer = null;
                }
                parent.OnStopTimer();
            }
 
            /// <include file='doc\UpDownBase.uex' path='docs/doc[@for="UpDownBase.UpDownButtons.TimerHandler"]/*' />
            /// <devdoc>
            ///     Generates updown events when the timer calls this function.
            /// </devdoc>
            private void TimerHandler(object source, EventArgs args) {
 
                // Make sure we've got mouse capture
                if (!Capture) {
                    EndButtonPress();
                    return;
                }
 
                // onUpDown method calls customer's ValueCHanged event handler which might enter the message loop and 
                // process the mouse button up event, which results in timer being disposed 
                OnUpDown(new UpDownEventArgs((int)pushed));
 
                if (timer != null) {
                    // Accelerate timer.
                    this.timerInterval *= 7;
                    this.timerInterval /= 10;
 
                    if (this.timerInterval < 1) {
                        this.timerInterval = 1;
                    }
 
                    timer.Interval = this.timerInterval;
                } 
            }
 
            internal class UpDownButtonsAccessibleObject : ControlAccessibleObject {
 
                private DirectionButtonAccessibleObject upButton;
                private DirectionButtonAccessibleObject downButton;
 
                public UpDownButtonsAccessibleObject(UpDownButtons owner) : base(owner) {
                }
 
                public override string Name {
                    get {
                        string baseName = base.Name;
                        if (baseName == null || baseName.Length == 0) {
                            if (AccessibilityImprovements.Level3) {
                                // For AI.Level3 spinner is already announced so use type name.
                                return Owner.ParentInternal.GetType().Name;
                            }
                            return SR.GetString(SR.SpinnerAccessibleName);
                        }
                        return baseName;
                    }
                    set {
                        base.Name = value;
                    }
                }
 
                /// <include file='doc\DomainUpDown.uex' path='docs/doc[@for="DomainUpDown.DomainUpDownAccessibleObject.Role"]/*' />
                /// <devdoc>
                ///    <para>[To be supplied.]</para>
                /// </devdoc>
                public override AccessibleRole Role {
                    get {
                        AccessibleRole role = Owner.AccessibleRole;
                        if (role != AccessibleRole.Default) {
                            return role;
                        }
                        return AccessibleRole.SpinButton;
                    }
                }
 
                private DirectionButtonAccessibleObject UpButton {
                    get {
                        if (upButton == null) {
                            upButton = new DirectionButtonAccessibleObject(this, true);
                        }
                        return upButton;
                    }
                }
 
                private DirectionButtonAccessibleObject DownButton {
                    get {
                        if (downButton == null) {
                            downButton = new DirectionButtonAccessibleObject(this, false);
                        }
                        return downButton;
                    }
                }
 
 
 
                /// <include file='doc\DomainUpDown.uex' path='docs/doc[@for="DomainUpDown.DomainUpDownAccessibleObject.GetChild"]/*' />
                /// <devdoc>
                /// </devdoc>
                public override AccessibleObject GetChild(int index) {
 
                    // Up button
                    //
                    if (index == 0) {
                        return UpButton;
                    }
 
                    // Down button
                    //
                    if (index == 1) {
                        return DownButton;
                    }
 
                    return null;
                }
 
                /// <include file='doc\DomainUpDown.uex' path='docs/doc[@for="DomainUpDown.DomainUpDownAccessibleObject.GetChildCount"]/*' />
                /// <devdoc>
                /// </devdoc>
                public override int GetChildCount() {
                    return 2;
                }
 
                internal class DirectionButtonAccessibleObject : AccessibleObject {
                    private bool up;
                    private UpDownButtonsAccessibleObject parent;
 
                    public DirectionButtonAccessibleObject(UpDownButtonsAccessibleObject parent, bool up) {
                        this.parent = parent;
                        this.up = up;
                    }
 
                    public override Rectangle Bounds {
                        get {
                            // Get button bounds
                            //
                            Rectangle bounds = ((UpDownButtons)parent.Owner).Bounds;
                            bounds.Height /= 2;
                            if (!up) {
                                bounds.Y += bounds.Height;
                            }
 
                            // Convert to screen co-ords
                            //
                            return (((UpDownButtons)parent.Owner).ParentInternal).RectangleToScreen(bounds);
                        }
                    }
 
                    public override string Name {
                        get {
                            if (up) {
                                return SR.GetString(SR.UpDownBaseUpButtonAccName);
                            }
                            return SR.GetString(SR.UpDownBaseDownButtonAccName);
                        }
                        set {
                        }
                    }
 
                    public override AccessibleObject Parent {
                        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                        get {
                            return parent;
                        }
                    }
 
                    public override AccessibleRole Role {
                        get {
                            return AccessibleRole.PushButton;
                        }
                    }
                }
            }
 
        } // end class UpDownButtons
 
        // Button identifiers
 
        internal enum ButtonID {
            None = 0,
            Up = 1,
            Down = 2,
        }
    } // end class UpDownBase
}