2 // System.Windows.Forms.TrackBar.cs
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 // Copyright (c) 2004-2006 Novell, Inc.
27 // Jordi Mas i Hernandez, jordi@ximian.com
28 // Rolf Bjarne Kvinge, RKvinge@novell.com
31 // - The AutoSize functionality seems quite broken for vertical controls in .Net 1.1. Not
32 // sure if we are implementing it the right way.
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
40 using System.Drawing.Imaging;
41 using System.Drawing.Drawing2D;
43 using System.Runtime.InteropServices;
45 namespace System.Windows.Forms
47 [DefaultBindingProperty ("Value")]
49 [ClassInterface (ClassInterfaceType.AutoDispatch)]
50 [Designer("System.Windows.Forms.Design.TrackBarDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
51 [DefaultEvent ("Scroll")]
52 [DefaultProperty("Value")]
53 public class TrackBar : Control, ISupportInitialize
57 internal int tickFrequency;
58 private bool autosize;
60 private int smallChange;
61 private int largeChange;
62 private Orientation orientation;
63 private TickStyle tickStyle;
64 private Rectangle thumb_pos = new Rectangle (); /* Current position and size of the thumb */
65 private Rectangle thumb_area = new Rectangle (); /* Area where the thumb can scroll */
66 internal bool thumb_pressed = false;
67 private System.Timers.Timer holdclick_timer = new System.Timers.Timer ();
68 internal int thumb_mouseclick;
69 private bool mouse_clickmove;
70 private bool is_moving_right; // which way the thumb should move when mouse is down (right=up, left=down)
71 internal int mouse_down_x_offset; // how far from left side of thumb was the mouse clicked.
72 internal bool mouse_moved; // has the mouse moved since it was clicked?
73 private const int size_of_autosize = 45;
74 private bool right_to_left_layout;
78 [EditorBrowsable (EditorBrowsableState.Always)]
80 public new event EventHandler AutoSizeChanged {
81 add {base.AutoSizeChanged += value;}
82 remove {base.AutoSizeChanged -= value;}
86 [EditorBrowsable (EditorBrowsableState.Never)]
87 public new event EventHandler BackgroundImageChanged {
88 add { base.BackgroundImageChanged += value; }
89 remove { base.BackgroundImageChanged -= value; }
92 [EditorBrowsable (EditorBrowsableState.Never)]
94 public new event EventHandler BackgroundImageLayoutChanged
96 add { base.BackgroundImageLayoutChanged += value; }
97 remove { base.BackgroundImageLayoutChanged -= value; }
101 [EditorBrowsable (EditorBrowsableState.Never)]
102 public new event EventHandler Click {
103 add { base.Click += value; }
104 remove { base.Click -= value; }
108 [EditorBrowsable (EditorBrowsableState.Never)]
109 public new event EventHandler DoubleClick {
110 add { base.DoubleClick += value; }
111 remove { base.DoubleClick -= value; }
115 [EditorBrowsable (EditorBrowsableState.Never)]
116 public new event EventHandler FontChanged {
117 add { base.FontChanged += value; }
118 remove { base.FontChanged -= value; }
122 [EditorBrowsable (EditorBrowsableState.Never)]
123 public new event EventHandler ForeColorChanged {
124 add { base.ForeColorChanged += value; }
125 remove { base.ForeColorChanged -= value; }
129 [EditorBrowsable (EditorBrowsableState.Never)]
130 public new event EventHandler ImeModeChanged {
131 add { base.ImeModeChanged += value; }
132 remove { base.ImeModeChanged -= value; }
136 [EditorBrowsable (EditorBrowsableState.Never)]
137 public new event MouseEventHandler MouseClick {
138 add {base.MouseClick += value;}
139 remove {base.MouseClick -= value;}
143 [EditorBrowsable (EditorBrowsableState.Never)]
144 public new event MouseEventHandler MouseDoubleClick
146 add { base.MouseDoubleClick += value; }
147 remove { base.MouseDoubleClick -= value; }
151 [EditorBrowsable (EditorBrowsableState.Never)]
152 public new event EventHandler PaddingChanged
154 add { base.PaddingChanged += value; }
155 remove { base.PaddingChanged -= value; }
159 [EditorBrowsable (EditorBrowsableState.Never)]
160 public new event PaintEventHandler Paint {
161 add { base.Paint += value; }
162 remove { base.Paint -= value; }
165 public event EventHandler RightToLeftLayoutChanged {
166 add {Events.AddHandler (RightToLeftLayoutChangedEvent, value);}
167 remove {Events.RemoveHandler (RightToLeftLayoutChangedEvent, value);}
171 [EditorBrowsable (EditorBrowsableState.Never)]
172 public new event EventHandler TextChanged {
173 add { base.TextChanged += value; }
174 remove { base.TextChanged -= value; }
177 static object RightToLeftLayoutChangedEvent = new object ();
178 static object ScrollEvent = new object ();
179 static object ValueChangedEvent = new object ();
181 public event EventHandler Scroll {
182 add { Events.AddHandler (ScrollEvent, value); }
183 remove { Events.RemoveHandler (ScrollEvent, value); }
186 public event EventHandler ValueChanged {
187 add { Events.AddHandler (ValueChangedEvent, value); }
188 remove { Events.RemoveHandler (ValueChangedEvent, value); }
193 #region UIA FrameWork Events
194 static object UIAValueParamChangedEvent = new object ();
196 internal event EventHandler UIAValueParamChanged {
197 add { Events.AddHandler (UIAValueParamChangedEvent, value); }
198 remove { Events.RemoveHandler (UIAValueParamChangedEvent, value); }
201 internal void OnUIAValueParamChanged ()
203 EventHandler eh = (EventHandler) Events [UIAValueParamChangedEvent];
205 eh (this, EventArgs.Empty);
211 orientation = Orientation.Horizontal;
217 tickStyle = TickStyle.BottomRight;
220 mouse_clickmove = false;
221 MouseDown += new MouseEventHandler (OnMouseDownTB);
222 MouseUp += new MouseEventHandler (OnMouseUpTB);
223 MouseMove += new MouseEventHandler (OnMouseMoveTB);
224 MouseLeave += new EventHandler (OnMouseLeave);
225 KeyDown += new KeyEventHandler (OnKeyDownTB);
226 LostFocus += new EventHandler (OnLostFocusTB);
227 GotFocus += new EventHandler (OnGotFocusTB);
228 holdclick_timer.Elapsed += new ElapsedEventHandler (OnFirstClickTimer);
230 SetStyle (ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.UseTextForAccessibility, false);
233 #region Private & Internal Properties
234 internal Rectangle ThumbPos {
244 internal Rectangle ThumbArea {
254 internal bool ThumbEntered {
255 get { return thumb_entered; }
257 if (thumb_entered == value)
259 thumb_entered = value;
260 if (ThemeEngine.Current.TrackBarHasHotThumbStyle)
261 Invalidate (GetRealThumbRectangle ());
264 #endregion // Private & Internal Properties
266 #region Public Properties
269 [EditorBrowsable (EditorBrowsableState.Always)]
270 [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
271 [DefaultValue (true)]
272 public override bool AutoSize {
273 get { return autosize; }
274 set { autosize = value;}
277 [EditorBrowsable (EditorBrowsableState.Never)]
279 public override Image BackgroundImage {
280 get { return base.BackgroundImage; }
281 set { base.BackgroundImage = value; }
284 [EditorBrowsable (EditorBrowsableState.Never)]
286 public override ImageLayout BackgroundImageLayout {
288 return base.BackgroundImageLayout;
291 base.BackgroundImageLayout = value;
295 protected override CreateParams CreateParams {
297 return base.CreateParams;
301 protected override ImeMode DefaultImeMode {
302 get {return ImeMode.Disable; }
305 protected override Size DefaultSize {
306 get { return ThemeEngine.Current.TrackBarDefaultSize; }
309 [EditorBrowsable (EditorBrowsableState.Never)]
310 protected override bool DoubleBuffered {
312 return base.DoubleBuffered;
315 base.DoubleBuffered = value;
320 [EditorBrowsable (EditorBrowsableState.Never)]
321 public override Font Font {
322 get { return base.Font; }
323 set { base.Font = value; }
326 [EditorBrowsable (EditorBrowsableState.Never)]
328 public override Color ForeColor {
329 get { return base.ForeColor; }
330 set { base.ForeColor = value; }
333 [EditorBrowsable (EditorBrowsableState.Never)]
335 public new ImeMode ImeMode {
336 get { return base.ImeMode; }
337 set { base.ImeMode = value; }
341 public int LargeChange
343 get { return largeChange; }
346 throw new ArgumentOutOfRangeException (string.Format ("Value '{0}' must be greater than or equal to 0.", value));
350 OnUIAValueParamChanged ();
355 [RefreshProperties (RefreshProperties.All)]
357 get { return maximum; }
359 if (maximum != value) {
362 if (maximum < minimum)
367 OnUIAValueParamChanged ();
373 [RefreshProperties (RefreshProperties.All)]
375 get { return minimum; }
378 if (Minimum != value) {
381 if (minimum > maximum)
386 OnUIAValueParamChanged ();
391 [DefaultValue (Orientation.Horizontal)]
393 public Orientation Orientation {
394 get { return orientation; }
396 if (!Enum.IsDefined (typeof (Orientation), value))
397 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for Orientation", value));
399 /* Orientation can be changed once the control has been created */
400 if (orientation != value) {
403 if (this.IsHandleCreated) {
404 Size = new Size (Height, Width);
412 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
413 [EditorBrowsable (EditorBrowsableState.Never)]
414 public new Padding Padding {
419 base.Padding = value;
424 [DefaultValue (false)]
425 public virtual bool RightToLeftLayout {
427 return right_to_left_layout;
430 if (value != right_to_left_layout) {
431 right_to_left_layout = value;
432 OnRightToLeftLayoutChanged (EventArgs.Empty);
438 public int SmallChange {
439 get { return smallChange;}
442 throw new ArgumentOutOfRangeException (string.Format ("Value '{0}' must be greater than or equal to 0.", value));
444 if (smallChange != value) {
447 OnUIAValueParamChanged ();
452 [EditorBrowsable (EditorBrowsableState.Never)]
455 public override string Text {
456 get { return base.Text; }
457 set { base.Text = value; }
461 public int TickFrequency {
462 get { return tickFrequency; }
465 tickFrequency = value;
471 [DefaultValue (TickStyle.BottomRight)]
472 public TickStyle TickStyle {
473 get { return tickStyle; }
475 if (!Enum.IsDefined (typeof (TickStyle), value))
476 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for TickStyle", value));
478 if (tickStyle != value) {
488 get { return position; }
490 SetValue (value, false);
494 void SetValue (int value, bool fire_onscroll)
496 if (value < Minimum || value > Maximum)
497 throw new ArgumentException(
498 String.Format ("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
500 if (position == value)
505 // OnScroll goes before OnValueChanged
507 OnScroll (EventArgs.Empty);
509 // XXX any reason we don't call OnValueChanged here?
510 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
512 eh (this, EventArgs.Empty);
514 Invalidate (thumb_area);
517 #endregion //Public Properties
519 #region Public Methods
521 public void BeginInit ()
526 protected override void CreateHandle ()
528 base.CreateHandle ();
531 protected override void SetBoundsCore (int x, int y,int width, int height, BoundsSpecified specified)
534 if (orientation == Orientation.Vertical) {
535 width = size_of_autosize;
537 height = size_of_autosize;
540 base.SetBoundsCore (x, y, width, height, specified);
543 public void EndInit ()
548 protected override bool IsInputKey (Keys keyData)
550 if ((keyData & Keys.Alt) == 0) {
551 switch (keyData & Keys.KeyCode) {
563 return base.IsInputKey (keyData);
566 protected override void OnBackColorChanged (EventArgs e)
568 base.OnBackColorChanged (e);
571 protected override void OnHandleCreated (EventArgs e)
573 base.OnHandleCreated (e);
576 if (Orientation == Orientation.Horizontal)
577 Size = new Size (Width, 40);
579 Size = new Size (50, Height);
581 UpdatePos (Value, true);
584 [EditorBrowsable (EditorBrowsableState.Advanced)]
585 protected override void OnMouseWheel (MouseEventArgs e)
587 base.OnMouseWheel (e);
589 if (!Enabled) return;
597 [EditorBrowsable (EditorBrowsableState.Advanced)]
598 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
600 EventHandler eh = (EventHandler)Events [RightToLeftLayoutChangedEvent];
605 protected virtual void OnScroll (EventArgs e)
607 EventHandler eh = (EventHandler)(Events [ScrollEvent]);
612 protected override void OnSystemColorsChanged (EventArgs e)
614 base.OnSystemColorsChanged (e);
618 protected virtual void OnValueChanged (EventArgs e)
620 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
625 public void SetRange (int minValue, int maxValue)
631 public override string ToString()
633 return string.Format("System.Windows.Forms.TrackBar, Minimum: {0}, Maximum: {1}, Value: {2}",
634 Minimum, Maximum, Value);
638 protected override void WndProc (ref Message m)
640 base.WndProc (ref m);
642 // Basically we want ControlStyles.ResizeRedraw but
643 // tests say we can't set that flag
644 if ((Msg)m.Msg == Msg.WM_WINDOWPOSCHANGED && Visible)
648 #endregion Public Methods
650 #region Private Methods
652 private void UpdatePos (int newPos, bool update_trumbpos)
654 if (newPos < minimum){
655 SetValue (minimum, true);
658 if (newPos > maximum) {
659 SetValue (maximum, true);
662 SetValue (newPos, true);
667 // Used by UIA implementation, so making internal
668 internal void LargeIncrement ()
670 UpdatePos (position + LargeChange, true);
671 Invalidate (thumb_area);
674 // Used by UIA implementation, so making internal
675 internal void LargeDecrement ()
677 UpdatePos (position - LargeChange, true);
678 Invalidate (thumb_area);
681 private void SmallIncrement ()
683 UpdatePos (position + SmallChange, true);
684 Invalidate (thumb_area);
687 private void SmallDecrement ()
689 UpdatePos (position - SmallChange, true);
690 Invalidate (thumb_area);
693 private void OnMouseUpTB (object sender, MouseEventArgs e)
695 if (!Enabled) return;
697 if (thumb_pressed == true || mouse_clickmove == true) {
698 thumb_pressed = false;
699 holdclick_timer.Enabled = false;
700 this.Capture = false;
701 Invalidate (thumb_area);
705 private void OnMouseDownTB (object sender, MouseEventArgs e)
707 if (!Enabled) return;
711 bool fire_timer = false;
713 Point point = new Point (e.X, e.Y);
715 if (orientation == Orientation.Horizontal) {
717 if (thumb_pos.Contains (point)) {
719 thumb_pressed = true;
720 thumb_mouseclick = e.X;
721 mouse_down_x_offset = e.X - thumb_pos.X;
722 Invalidate (thumb_area);
725 if (thumb_area.Contains (point)) {
726 is_moving_right = e.X > thumb_pos.X + thumb_pos.Width;
732 Invalidate (thumb_area);
734 mouse_clickmove = true;
739 Rectangle vertical_thumb_pos = thumb_pos;
740 vertical_thumb_pos.Width = thumb_pos.Height;
741 vertical_thumb_pos.Height = thumb_pos.Width;
742 if (vertical_thumb_pos.Contains (point)) {
744 thumb_pressed = true;
745 thumb_mouseclick = e.Y;
746 mouse_down_x_offset = e.Y - thumb_pos.Y;
747 Invalidate (thumb_area);
750 if (thumb_area.Contains (point)) {
751 is_moving_right = e.Y > thumb_pos.Y + thumb_pos.Width;
757 Invalidate (thumb_area);
759 mouse_clickmove = true;
765 holdclick_timer.Interval = 300;
766 holdclick_timer.Enabled = true;
770 private void OnMouseMoveTB (object sender, MouseEventArgs e)
772 if (!Enabled) return;
776 /* Moving the thumb */
778 SetValue (ThemeEngine.Current.TrackBarValueFromMousePosition (e.X, e.Y, this), true);
780 ThumbEntered = GetRealThumbRectangle ().Contains (e.Location);
783 Rectangle GetRealThumbRectangle ()
785 Rectangle result = thumb_pos;
786 if (Orientation == Orientation.Vertical) {
787 result.Width = thumb_pos.Height;
788 result.Height = thumb_pos.Width;
793 internal override void OnPaintInternal (PaintEventArgs pevent)
795 ThemeEngine.Current.DrawTrackBar (pevent.Graphics, pevent.ClipRectangle, this);
798 private void OnLostFocusTB (object sender, EventArgs e)
803 private void OnGotFocusTB (object sender, EventArgs e)
807 private void OnKeyDownTB (object sender, KeyEventArgs e)
809 bool horiz = Orientation == Orientation.Horizontal;
844 SetValue (Minimum, true);
846 SetValue (Maximum, true);
851 SetValue (Maximum, true);
853 SetValue (Minimum, true);
861 private void OnFirstClickTimer (Object source, ElapsedEventArgs e)
864 pnt = PointToClient (MousePosition);
866 On Win32 the thumb only moves in one direction after a click,
867 if the thumb passes the clicked point it will never go in the
868 other way unless the mouse is released and clicked again. This
869 is also true if the mouse moves while beeing hold down.
872 if (thumb_area.Contains (pnt)) {
873 bool invalidate = false;
874 if (orientation == Orientation.Horizontal) {
875 if (pnt.X > thumb_pos.X + thumb_pos.Width && is_moving_right) {
878 } else if (pnt.X < thumb_pos.X && !is_moving_right) {
883 if (pnt.Y > thumb_pos.Y + thumb_pos.Width && is_moving_right) {
886 } else if (pnt.Y < thumb_pos.Y && !is_moving_right) {
892 // A Refresh is necessary because the mouse is down and if we just invalidate
893 // we'll only get paint events once in a while.
898 void OnMouseLeave (object sender, EventArgs e)
900 ThumbEntered = false;
902 #endregion // Private Methods