File: winforms\Managed\System\WinForms\TrackBar.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
 //------------------------------------------------------------------------------
// <copyright file="TrackBar.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 */
namespace System.Windows.Forms {
    using System.Runtime.Serialization.Formatters;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
 
    using System.Diagnostics;
 
    using System;
    using System.Security.Permissions;
    using Microsoft.Win32;
    using System.ComponentModel;
    using System.Drawing;
    using System.Windows.Forms;
    using System.ComponentModel.Design;
    using System.Windows.Forms.Layout;
    using System.Globalization;
    
    /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar"]/*' />
    /// <devdoc>
    ///     The TrackBar is a scrollable control similar to the ScrollBar, but
    ///     has a different UI.  You can configure ranges through which it should
    ///     scroll, and also define increments for off-button clicks.  It can be
    ///     aligned horizontally or vertically.  You can also configure how many
    ///     'ticks' are shown for the total range of values
    /// </devdoc>
    [
    ComVisible(true),
    ClassInterface(ClassInterfaceType.AutoDispatch),
    DefaultProperty("Value"),
    DefaultEvent("Scroll"),
    DefaultBindingProperty("Value"),
    Designer("System.Windows.Forms.Design.TrackBarDesigner, " + AssemblyRef.SystemDesign),
    SRDescription(SR.DescriptionTrackBar)
    ]
    public class TrackBar : Control, ISupportInitialize {
 
        private static readonly object EVENT_SCROLL = new object();
        private static readonly object EVENT_VALUECHANGED = new object();
        private static readonly object EVENT_RIGHTTOLEFTLAYOUTCHANGED = new object();        
 
        private bool autoSize = true;
        private int largeChange = 5;
        private int maximum = 10;
        private int minimum = 0;
        private Orientation orientation = System.Windows.Forms.Orientation.Horizontal;
        private int value = 0;
        private int smallChange = 1;
        private int tickFrequency = 1;
        private TickStyle tickStyle = System.Windows.Forms.TickStyle.BottomRight;
 
        private int requestedDim;
 
        // Mouse wheel movement
        private int cumulativeWheelData = 0;
 
        // Disable value range checking while initializing the control
        private bool initializing = false;
 
        private bool rightToLeftLayout = false;        
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.TrackBar"]/*' />
        /// <devdoc>
        ///     Creates a new TrackBar control with a default range of 0..10 and
        ///     ticks shown every value.
        /// </devdoc>
        public TrackBar()
        : base() {
            SetStyle(ControlStyles.UserPaint, false);
            SetStyle(ControlStyles.UseTextForAccessibility, false);
            requestedDim = PreferredDimension;
        }
        
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.AutoSize"]/*' />
        /// <devdoc>
        ///     Indicates if the control is being auto-sized.  If true, the
        ///     TrackBar will adjust either its height or width [depending on
        ///     orientation] to make sure that only the required amount of
        ///     space is used.
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(true),
        SRDescription(SR.TrackBarAutoSizeDescr),
        Browsable(true),
        EditorBrowsable(EditorBrowsableState.Always),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)
        ]
        public override bool AutoSize {
            get {
                return autoSize;
            }
 
            set {
                // Note that we intentionally do not call base.  Labels size themselves by
                // overriding SetBoundsCore (old RTM code).  We let CommonProperties.GetAutoSize
                // continue to return false to keep our LayoutEngines from messing with TextBoxes.
                // This is done for backwards compatibility since the new AutoSize behavior differs.                
                if (autoSize != value) {
                    autoSize = value;
                    if (orientation == Orientation.Horizontal) {
                        SetStyle(ControlStyles.FixedHeight, autoSize);
                        SetStyle(ControlStyles.FixedWidth, false);
                    }
                    else {
                        SetStyle(ControlStyles.FixedWidth, autoSize);
                        SetStyle(ControlStyles.FixedHeight, false);
                    }
                    AdjustSize();
                    OnAutoSizeChanged(EventArgs.Empty);
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.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\TrackBar.uex' path='docs/doc[@for="TrackBar.BackgroundImage"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override Image BackgroundImage {
            get {
                return base.BackgroundImage;
            }
            set {
                base.BackgroundImage = value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.BackgroundImageChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler BackgroundImageChanged {
            add {
                base.BackgroundImageChanged += value;
            }
            remove {
                base.BackgroundImageChanged -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.BackgroundImageLayout"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override ImageLayout BackgroundImageLayout {
            get {
                return base.BackgroundImageLayout;
            }
            set {
                base.BackgroundImageLayout = value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.BackgroundImageLayoutChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler BackgroundImageLayoutChanged {
            add {
                base.BackgroundImageLayoutChanged += value;
            }
            remove {
                base.BackgroundImageLayoutChanged -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.CreateParams"]/*' />
        /// <devdoc>
        ///     This is called when creating a window.  Inheriting classes can override
        ///     this to add extra functionality, but should not forget to first call
        ///     base.getCreateParams() to make sure the control continues to work
        ///     correctly.
        /// </devdoc>
        /// <internalonly/>
        protected override CreateParams CreateParams {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
            get {
                CreateParams cp = base.CreateParams;
                cp.ClassName = NativeMethods.WC_TRACKBAR;
 
                switch (tickStyle) {
                    case TickStyle.None:
                        cp.Style |= NativeMethods.TBS_NOTICKS;
                        break;
                    case TickStyle.TopLeft:
                        cp.Style |= (NativeMethods.TBS_AUTOTICKS | NativeMethods.TBS_TOP);
                        break;
                    case TickStyle.BottomRight:
                        cp.Style |= (NativeMethods.TBS_AUTOTICKS | NativeMethods.TBS_BOTTOM);
                        break;
                    case TickStyle.Both:
                        cp.Style |= (NativeMethods.TBS_AUTOTICKS | NativeMethods.TBS_BOTH);
                        break;
                }
 
                if (orientation == Orientation.Vertical) {
                    cp.Style |= NativeMethods.TBS_VERT; // HORIZ == 0
                }
 
                if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) {
                    //We want to turn on mirroring for Trackbar explicitly.
                    cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT;
                    //Don't need these styles when mirroring is turned on.
                    cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR);
                }
                return cp;
            }
        }
        
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.DefaultImeMode"]/*' />
        /// <internalonly/>
        protected override ImeMode DefaultImeMode {
            get {
                return ImeMode.Disable;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.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(104, PreferredDimension);
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.DoubleBuffered"]/*' />
        /// <devdoc>
        ///     This property is overridden and hidden from statement completion
        ///     on controls that are based on Win32 Native Controls.
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected override bool DoubleBuffered {
            get {
                return base.DoubleBuffered;
            }
            set {
                base.DoubleBuffered = value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.Font"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override Font Font {
            get {
                return base.Font;
            }
            set {
                base.Font = value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.FontChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler FontChanged {
            add {
                base.FontChanged += value;
            }
            remove {
                base.FontChanged -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.ForeColor"]/*' />
        /// <devdoc>
        ///     The current foreground color of the TrackBar.  Note that users
        ///     are unable to change this.  It is always Color.WINDOWTEXT
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override Color ForeColor {
            get {
                return SystemColors.WindowText;
            }
            set {
            }
        }
        
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.ForeColorChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler ForeColorChanged {
            add {
                base.ForeColorChanged += value;
            }
            remove {
                base.ForeColorChanged -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.ImeMode"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public ImeMode ImeMode {
            get {
                return base.ImeMode;
            }
            set {
                base.ImeMode = value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.ImeModeChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler ImeModeChanged {
            add {
                base.ImeModeChanged += value;
            }
            remove {
                base.ImeModeChanged -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.LargeChange"]/*' />
        /// <devdoc>
        ///     The number of ticks by which the TrackBar will change when an
        ///     event considered a "large change" occurs.  These include, Clicking the
        ///     mouse to the side of the button, or using the PgUp/PgDn keys on the
        ///     keyboard.
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(5),
        SRDescription(SR.TrackBarLargeChangeDescr)
        ]
        public int LargeChange {
            get {
                return largeChange;
            }
            set {
                if (value < 0) {
                    throw new ArgumentOutOfRangeException("LargeChange", SR.GetString(SR.TrackBarLargeChangeError, value));
                }
 
                if (largeChange != value) {
                    largeChange = value;
                    if (IsHandleCreated)
                    {
                        SendMessage(NativeMethods.TBM_SETPAGESIZE, 0, value);
                    }
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.Maximum"]/*' />
        /// <devdoc>
        ///     The upper limit of the range this TrackBar is working with.
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(10),
        RefreshProperties(RefreshProperties.All),
        SRDescription(SR.TrackBarMaximumDescr)
        ]
        public int Maximum {
            get {
                return maximum;
            }
            set {
                if (maximum != value) {
                    if (value < minimum) {
                        minimum = value;
                    }
                    SetRange(minimum, value);
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.Minimum"]/*' />
        /// <devdoc>
        ///     The lower limit of the range this TrackBar is working with.
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(0),
        RefreshProperties(RefreshProperties.All),
        SRDescription(SR.TrackBarMinimumDescr)
        ]
        public int Minimum {
            get {
                return minimum;
            }
            set {
                if (minimum != value) {
                    if (value > maximum) {
                        maximum = value;
                    }
                    SetRange(value, maximum);
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.Orientation"]/*' />
        /// <devdoc>
        ///    <para>The orientation for this TrackBar. Valid values are from
        ///       the Orientation enumeration. The control currently supports being
        ///       oriented horizontally and vertically.</para>
        /// </devdoc>
        [
        SRCategory(SR.CatAppearance),
        DefaultValue(Orientation.Horizontal),
        Localizable(true),
        SRDescription(SR.TrackBarOrientationDescr)
        ]
        public Orientation Orientation {
            get {
                return orientation;
            }
            set {
                //valid values are 0x0 to 0x1
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)Orientation.Horizontal, (int)Orientation.Vertical))
                {
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(Orientation));
                }
 
                if (orientation != value) {
                    orientation = value;
 
                    if (orientation == Orientation.Horizontal) {
                        SetStyle(ControlStyles.FixedHeight, autoSize);
                        SetStyle(ControlStyles.FixedWidth, false);
                        Width = requestedDim;
                    }
                    else {
                        SetStyle(ControlStyles.FixedHeight, false);
                        SetStyle(ControlStyles.FixedWidth, autoSize);
                        Height = requestedDim;
                    }
 
                    if (IsHandleCreated) {
                        Rectangle r = Bounds;
                        RecreateHandle();
                        SetBounds(r.X, r.Y, r.Height, r.Width, BoundsSpecified.All);
                        AdjustSize();
                    }
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.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)
        ]
        public new event EventHandler PaddingChanged {
            add { base.PaddingChanged += value; }
            remove { base.PaddingChanged -= value; }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.PreferredDimension"]/*' />
        /// <devdoc>
        ///     Little private routine that helps with auto-sizing.
        /// </devdoc>
        /// <internalonly/>
        private int PreferredDimension {
            get {
                int cyhscroll = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYHSCROLL);
 
                // this is our preferred size
                //
                return((cyhscroll * 8) / 3);
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.PreferredDimension"]/*' />
        /// <devdoc>
        ///     Redraw control, if the handle's created
        /// </devdoc>
        /// <internalonly/>
        private void RedrawControl()
        {
            if (IsHandleCreated)
            {
                //The '1' in the call to SendMessage below indicates that the 
                //trackbar should be redrawn (see TBM_SETRANGEMAX in MSDN)
                SendMessage(NativeMethods.TBM_SETRANGEMAX, 1, maximum);
                Invalidate();
            }
        }
 
        /// <include file='doc\Trackbar.uex' path='docs/doc[@for="Trackbar.RightToLeftLayout"]/*' />
        /// <devdoc>
        ///     This is used for international applications where the language
        ///     is written from RightToLeft. When this property is true,
        //      and the RightToLeft property is true, mirroring will be turned on on the trackbar.
        /// </devdoc>
        [
        SRCategory(SR.CatAppearance),
        Localizable(true),
        DefaultValue(false),
        SRDescription(SR.ControlRightToLeftLayoutDescr)
        ]
        public virtual bool RightToLeftLayout {
            get {
 
                return rightToLeftLayout;
            }
 
            set {
                if (value != rightToLeftLayout) {
                    rightToLeftLayout = value;
                    using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) {
                        OnRightToLeftLayoutChanged(EventArgs.Empty);
                    }
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.SmallChange"]/*' />
        /// <devdoc>
        ///     The number of ticks by which the TrackBar will change when an
        ///     event considered a "small change" occurs.  These are most commonly
        ///     seen by using the arrow keys to move the TrackBar thumb around.
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(1),
        SRDescription(SR.TrackBarSmallChangeDescr)
        ]
        public int SmallChange {
            get {
                return smallChange;
            }
            set {
                if (value < 0) {
                    throw new ArgumentOutOfRangeException("SmallChange", SR.GetString(SR.TrackBarSmallChangeError, value));
                }
                if (smallChange != value) {
                    smallChange = value;
                    if (IsHandleCreated)
                    {
                        SendMessage(NativeMethods.TBM_SETLINESIZE, 0, value);
                    }
                }
            }
        }
        
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.Text"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>        
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)]        
        public override string Text {
            get {
                return base.Text;
            }
            set {
                base.Text = value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.TextChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler TextChanged {
            add {
                base.TextChanged += value;
            }
            remove {
                base.TextChanged -= value;
            }
        }
        
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.TickStyle"]/*' />
        /// <devdoc>
        ///     Indicates how the TrackBar control will draw itself.  This affects
        ///     both where the ticks will be drawn in relation to the moveable thumb,
        ///     and how the thumb itself will be drawn.  values are taken from the
        ///     TickStyle enumeration.
        /// </devdoc>
        [
        SRCategory(SR.CatAppearance),
        DefaultValue(TickStyle.BottomRight),
        SRDescription(SR.TrackBarTickStyleDescr)
        ]
        public TickStyle TickStyle {
            get {
                return tickStyle;
            }
            set {
                // Confirm that value is a valid enum
                //valid values are 0x0 to 0x3
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)TickStyle.None, (int)TickStyle.Both))
                {
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(TickStyle));
                }
 
                if (tickStyle != value) {
                    tickStyle = value;
                    RecreateHandle();
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.TickFrequency"]/*' />
        /// <devdoc>
        ///     Indicates just how many ticks will be drawn.  For a TrackBar with a
        ///     range of 0..100, it might be impractical to draw all 100 ticks for a
        ///     very small control.  Passing in a value of 5 here would only draw
        ///     20 ticks -- i.e. Each tick would represent 5 units in the TrackBars
        ///     range of values.
        /// </devdoc>
        [
        SRCategory(SR.CatAppearance),
        DefaultValue(1),
        SRDescription(SR.TrackBarTickFrequencyDescr)
        ]
        public int TickFrequency {
            get {
                return tickFrequency;
            }
            set {
                // SEC. 
 
                if (tickFrequency != value) {
                    tickFrequency = value;
                    if (IsHandleCreated) {
                        SendMessage(NativeMethods.TBM_SETTICFREQ, value, 0);
                        Invalidate();
                    }
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.Value"]/*' />
        /// <devdoc>
        ///     The current location of the TrackBar thumb.  This value must
        ///     be between the lower and upper limits of the TrackBar range, of course.
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(0),
        Bindable(true),
        SRDescription(SR.TrackBarValueDescr)
        ]
        public int Value {
            get {
                GetTrackBarValue();
                return value;
            }
            set {
                if (this.value != value) {
                    if (!initializing && ((value < minimum) || (value > maximum)))
                        throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", (value).ToString(CultureInfo.CurrentCulture), "'Minimum'", "'Maximum'"));
                    this.value = value;
                    SetTrackBarPosition();
                    OnValueChanged(EventArgs.Empty);
                }
            }
        }    
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.Click"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler Click {
            add {
                base.Click += value;
            }
            remove {
                base.Click -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.DoubleClick"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler DoubleClick {
            add {
                base.DoubleClick += value;
            }
            remove {
                base.DoubleClick -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.MouseClick"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event MouseEventHandler MouseClick {
            add {
                base.MouseClick += value;
            }
            remove {
                base.MouseClick -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.MouseDoubleClick"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event MouseEventHandler MouseDoubleClick {
            add {
                base.MouseDoubleClick += value;
            }
            remove {
                base.MouseDoubleClick -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.RightToLeftLayoutChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)]
        public event EventHandler RightToLeftLayoutChanged {
            add {
                Events.AddHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value);
            }
            remove {
                Events.RemoveHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value);
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.Scroll"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SRCategory(SR.CatBehavior), SRDescription(SR.TrackBarOnScrollDescr)]
        public event EventHandler Scroll {
            add {
                Events.AddHandler(EVENT_SCROLL, value);
            }
            remove {
                Events.RemoveHandler(EVENT_SCROLL, value);
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.OnPaint"]/*' />
        /// <devdoc>
        ///     TrackBar Onpaint.
        /// </devdoc>
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event PaintEventHandler Paint {
            add {
                base.Paint += value;
            }
            remove {
                base.Paint -= value;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.ValueChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SRCategory(SR.CatAction), SRDescription(SR.valueChangedEventDescr)]
        public event EventHandler ValueChanged {
            add {
                Events.AddHandler(EVENT_VALUECHANGED, value);
            }
            remove {
                Events.RemoveHandler(EVENT_VALUECHANGED, value);
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.AdjustSize"]/*' />
        /// <devdoc>
        ///     Enforces autoSizing
        /// </devdoc>
        /// <internalonly/>
        private void AdjustSize() {
            if (IsHandleCreated) {
                int saveDim = requestedDim;
                try {
                    if (orientation == Orientation.Horizontal)
                        Height = autoSize ? PreferredDimension : saveDim;
                    else
                        Width = autoSize ? PreferredDimension : saveDim;
                }
                finally {
                    requestedDim = saveDim;
                }
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.BeginInit"]/*' />
        /// <devdoc>
        ///      Handles tasks required when the control is being initialized.
        /// </devdoc>
        /// <internalonly/>
        public void BeginInit() {
            initializing = true;
        }
 
        // Constrain the current value of the control to be within
        // the minimum and maximum.
        //
        private void ConstrainValue() {
 
            // Don't constrain the value while we're initializing the control
            if (initializing) {
                return;
            }
 
            Debug.Assert(minimum <= maximum, "Minimum should be <= Maximum");
 
            // Keep the current value within the minimum and maximum
            if (Value < minimum) {
                Value = minimum;
            }
            if (Value > maximum) {
                Value = maximum;
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.CreateHandle"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        protected override void CreateHandle() {
            if (!RecreatingHandle) {
                IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate();
                try {
                    NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX();
                    icc.dwICC = NativeMethods.ICC_BAR_CLASSES;
                    SafeNativeMethods.InitCommonControlsEx(icc);
                } finally {
                    UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
                }
            }
            base.CreateHandle();
        }
        
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.EndInit"]/*' />
        /// <devdoc>
        ///      Called when initialization of the control is complete.
        /// </devdoc>
        /// <internalonly/>
        public void EndInit() {
            initializing = false;
 
            // Make sure the value is constrained by the minimum and maximum
            ConstrainValue();
        }
 
        private void GetTrackBarValue() {
            if (IsHandleCreated) {
                value = unchecked( (int) (long)SendMessage(NativeMethods.TBM_GETPOS, 0, 0));
                
                // See SetTrackBarValue() for a description of why we sometimes reflect the trackbar value
                //                   
                   
                if (orientation == Orientation.Vertical) {
                    // Reflect value
                    value = Minimum + Maximum - value;
                }
                
                // Reflect for a RightToLeft horizontal trackbar
                //
                if (orientation == Orientation.Horizontal && RightToLeft == RightToLeft.Yes && !IsMirrored) {
                    value = Minimum + Maximum - value;
                }
            }
        }                                               
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.IsInputKey"]/*' />
        /// <devdoc>
        ///      Handling special input keys, such as pgup, pgdown, home, end, etc...
        /// </devdoc>
        protected override bool IsInputKey(Keys keyData) {
            if ((keyData & Keys.Alt) == Keys.Alt) return false;
            switch (keyData & Keys.KeyCode) {
                case Keys.PageUp:
                case Keys.PageDown:
                case Keys.Home:
                case Keys.End:
                    return true;
            }
            return base.IsInputKey(keyData);
        }
        
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.OnHandleCreated"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        protected override void OnHandleCreated(EventArgs e) {
            base.OnHandleCreated(e);
            SendMessage(NativeMethods.TBM_SETRANGEMIN, 0, minimum);
            SendMessage(NativeMethods.TBM_SETRANGEMAX, 0, maximum);
            SendMessage(NativeMethods.TBM_SETTICFREQ, tickFrequency, 0);
            SendMessage(NativeMethods.TBM_SETPAGESIZE, 0, largeChange);
            SendMessage(NativeMethods.TBM_SETLINESIZE, 0, smallChange);
            SetTrackBarPosition();
            AdjustSize();
        }
 
        /// <include file='doc\Form.uex' path='docs/doc[@for="Form.OnRightToLeftLayoutChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected virtual void OnRightToLeftLayoutChanged(EventArgs e) {
            if (GetAnyDisposingInHierarchy()) {
                return;
            }
 
            if (RightToLeft == RightToLeft.Yes) {
                RecreateHandle();
            }
 
            EventHandler eh = Events[EVENT_RIGHTTOLEFTLAYOUTCHANGED] as EventHandler;
            if (eh != null) {
                 eh(this, e);
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.OnScroll"]/*' />
        /// <devdoc>
        ///     Actually fires the "scroll" event.  Inheriting classes should override
        ///     this method in favor of actually adding an EventHandler for this
        ///     event.  Inheriting classes should not forget to call
        ///     base.onScroll(e)
        /// </devdoc>
        protected virtual void OnScroll(EventArgs e) {
            EventHandler handler = (EventHandler)Events[EVENT_SCROLL];
            if (handler != null) handler(this,e);
        }
 
        /// <include file='doc\Trackbar.uex' path='docs/doc[@for="Trackbar.OnMouseWheel"]/*' />
        /// <devdoc>
        /// <para>Raises the <see cref='System.Windows.Forms.Control.MouseWheel'/> event.</para>
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        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.cumulativeWheelData > -NativeMethods.WHEEL_DELTA, "cumulativeWheelData is too small");
            Debug.Assert(this.cumulativeWheelData < NativeMethods.WHEEL_DELTA, "cumulativeWheelData is too big");
            this.cumulativeWheelData += e.Delta;
 
            float partialNotches;
            partialNotches = (float)this.cumulativeWheelData / (float)NativeMethods.WHEEL_DELTA;
 
            if (wheelScrollLines == -1) {
                wheelScrollLines = TickFrequency;
            }
 
            // Evaluate number of bands to scroll
            int scrollBands = (int)((float)wheelScrollLines * partialNotches);
 
            if (scrollBands != 0) {
               int absScrollBands;
               if (scrollBands > 0) {
                  absScrollBands = scrollBands;
                  Value = Math.Min(absScrollBands+Value, Maximum);
                  this.cumulativeWheelData -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines));
               }
               else {
                  absScrollBands = -scrollBands;
                  Value = Math.Max(Value-absScrollBands, Minimum);
                  this.cumulativeWheelData -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines));
               }
            }
 
            if (e.Delta != Value) {
                OnScroll(EventArgs.Empty);
                OnValueChanged(EventArgs.Empty);
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.OnValueChanged"]/*' />
        /// <devdoc>
        ///     Actually fires the "valueChanged" event.
        /// </devdoc>
        /// <internalonly/>
        protected virtual void OnValueChanged(EventArgs e) {
            EventHandler handler = (EventHandler)Events[EVENT_VALUECHANGED];
            if (handler != null) handler(this,e);
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.OnBackColorChanged"]/*' />
        /// <devdoc>
        ///     This method is called by the control when any property changes. Inheriting
        ///     controls can overide this method to get property change notification on
        ///     basic properties. Inherting controls must call base.propertyChanged.
        /// </devdoc>
        protected override void OnBackColorChanged(EventArgs e) {
            base.OnBackColorChanged(e);
            RedrawControl();
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.OnSystemColorsChanged"]/*' />
        protected override void OnSystemColorsChanged (EventArgs e) {
            base.OnSystemColorsChanged (e);
            RedrawControl();
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.SetBoundsCore"]/*' />
        /// <devdoc>
        ///     Overrides Control.setBoundsCore to enforce autoSize.
        /// </devdoc>
        protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
            //SetBoundsCore .. sets the height for a control in designer .. we should obey the requested 
            //height is Autosize is false..
            //if (IsHandleCreated) {
            requestedDim = (orientation == Orientation.Horizontal)
                            ? height
                            : width;
 
            if (autoSize) {
                if (orientation == Orientation.Horizontal) {
                    if ((specified & BoundsSpecified.Height) != BoundsSpecified.None)
                        height = PreferredDimension;
                }
                else {
                    if ((specified & BoundsSpecified.Width) != BoundsSpecified.None)
                        width = PreferredDimension;
                }
            }
            //}
            base.SetBoundsCore(x, y, width, height, specified);
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.SetRange"]/*' />
        /// <devdoc>
        ///     Lets you set the the entire range for the TrackBar control at once.
        ///     The values passed are both the lower and upper limits to the range
        ///     with which the control will work.
        /// </devdoc>
        public void SetRange(int minValue, int maxValue) {
            if (minimum != minValue || maximum != maxValue) {
            
                // The Minimum and Maximum properties contain the logic for
                // ensuring that minValue <= maxValue. It is possible, however,
                // that this function will be called somewhere other than from
                // these two properties, so we'll check that here anyway.
                // 
                if (minValue > maxValue) {
                    // We'll just adjust maxValue to match minValue
                    maxValue = minValue;
                }
 
                minimum = minValue;
                maximum = maxValue;
 
                if (IsHandleCreated) {
                    SendMessage(NativeMethods.TBM_SETRANGEMIN, 0, minimum);
 
                    // We must repaint the trackbar after changing
                    // the range. The '1' in the call to
                    // SendMessage below indicates that the trackbar
                    // should be redrawn (see TBM_SETRANGEMAX in MSDN)
                    SendMessage(NativeMethods.TBM_SETRANGEMAX, 1, maximum);
 
                    Invalidate();
                }
                
                // When we change the range, the comctl32 trackbar's internal position can change 
                // (because of the reflection that occurs with vertical trackbars)
                // so we make sure to explicitly set the trackbar position.
                //
                if (value < minimum) {
                    value = minimum;
                }
                if (value > maximum) {
                    value = maximum;
                }
                SetTrackBarPosition();
            }
        }
        
        private void SetTrackBarPosition() {
            if (IsHandleCreated) {
            
                // There are two situations where we want to reflect the track bar position:
                //
                // 1. For a vertical trackbar, it seems to make more sense for the trackbar to increase in value
                //    as the slider moves up the trackbar (this is opposite what the underlying winctl control does)
                //
                // 2. For a RightToLeft horizontal trackbar, we want to reflect the position.
                //
                int reflectedValue = value;
                
                // 1. Reflect for a vertical trackbar
                //
                if (orientation == Orientation.Vertical) {
                    reflectedValue = Minimum + Maximum - value;
                }
                
                // 2. Reflect for a RightToLeft horizontal trackbar
                //
                if (orientation == Orientation.Horizontal && RightToLeft == RightToLeft.Yes && !IsMirrored) {
                    reflectedValue = Minimum + Maximum - value;
                }
                
                SendMessage(NativeMethods.TBM_SETPOS, 1, reflectedValue);
            }
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.ToString"]/*' />
        /// <devdoc>
        ///     Returns a string representation for this control.
        /// </devdoc>
        /// <internalonly/>
        public override string ToString() {
 
            string s = base.ToString();
            return s + ", Minimum: " + Minimum.ToString(CultureInfo.CurrentCulture) + ", Maximum: " + Maximum.ToString(CultureInfo.CurrentCulture) + ", Value: " + Value.ToString(CultureInfo.CurrentCulture);
        }
 
        /// <include file='doc\TrackBar.uex' path='docs/doc[@for="TrackBar.WndProc"]/*' />
        /// <devdoc>
        ///     The button's window procedure.  Inheriting classes can override this
        ///     to add extra functionality, but should not forget to call
        ///     base.wndProc(m); to ensure the button continues to function properly.
        /// </devdoc>
        /// <internalonly/>
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m) {
            switch (m.Msg) {
                case NativeMethods.WM_REFLECT+NativeMethods.WM_HSCROLL:
                case NativeMethods.WM_REFLECT+NativeMethods.WM_VSCROLL:
                    switch (NativeMethods.Util.LOWORD(m.WParam)) {
                        case NativeMethods.TB_LINEUP:
                        case NativeMethods.TB_LINEDOWN:
                        case NativeMethods.TB_PAGEUP:
                        case NativeMethods.TB_PAGEDOWN:
                            //case NativeMethods.TB_THUMBPOSITION:
                        case NativeMethods.TB_THUMBTRACK:
                        case NativeMethods.TB_TOP:
                        case NativeMethods.TB_BOTTOM:
                        case NativeMethods.TB_ENDTRACK:
                            if (value != Value) {
                                OnScroll(EventArgs.Empty);
                                OnValueChanged(EventArgs.Empty);
                            }
                            break;
                    }
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }
    }
}