2 // System.Windows.Forms.ScrollBar.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.
23 // Copyright (C) 2004-2005, Novell, Inc.
26 // Jordi Mas i Hernandez jordi@ximian.com
33 using System.Drawing.Imaging;
34 using System.Drawing.Drawing2D;
35 using System.ComponentModel;
36 using System.Runtime.InteropServices;
38 namespace System.Windows.Forms
41 [ClassInterface (ClassInterfaceType.AutoDispatch)]
42 [DefaultEvent ("Scroll")]
43 [DefaultProperty ("Value")]
44 public abstract class ScrollBar : Control
46 #region Local Variables
50 private int large_change;
51 private int small_change;
52 internal int scrollbutton_height;
53 internal int scrollbutton_width;
54 private Rectangle first_arrow_area = new Rectangle (); // up or left
55 private Rectangle second_arrow_area = new Rectangle (); // down or right
56 private Rectangle thumb_pos = new Rectangle ();
57 private Rectangle thumb_area = new Rectangle ();
58 internal ButtonState firstbutton_state = ButtonState.Normal;
59 internal ButtonState secondbutton_state = ButtonState.Normal;
60 private bool firstbutton_pressed = false;
61 private bool secondbutton_pressed = false;
62 private bool thumb_pressed = false;
63 private float pixel_per_pos = 0;
64 private Timer timer = new Timer ();
65 private TimerType timer_type;
66 private int thumb_size = 40;
67 private const int thumb_min_size = 8;
68 private const int thumb_notshown_size = 40;
69 internal bool use_manual_thumb_size;
70 internal int manual_thumb_size;
72 internal bool implicit_control;
73 private int lastclick_pos; // Position of the last button-down event
74 private int thumbclick_offset; // Position of the last button-down event relative to the thumb edge
75 private Rectangle dirty;
77 internal ThumbMoving thumb_moving = ThumbMoving.None;
78 bool first_button_entered;
79 bool second_button_entered;
81 #endregion // Local Variables
83 private enum TimerType
91 internal enum ThumbMoving
100 [EditorBrowsable (EditorBrowsableState.Never)]
101 public new event EventHandler AutoSizeChanged {
102 add { base.AutoSizeChanged += value; }
103 remove { base.AutoSizeChanged -= value; }
107 [EditorBrowsable (EditorBrowsableState.Never)]
108 public new event EventHandler BackColorChanged {
109 add { base.BackColorChanged += value; }
110 remove { base.BackColorChanged -= value; }
114 [EditorBrowsable (EditorBrowsableState.Never)]
115 public new event EventHandler BackgroundImageChanged {
116 add { base.BackgroundImageChanged += value; }
117 remove { base.BackgroundImageChanged -= value; }
121 [EditorBrowsable (EditorBrowsableState.Never)]
122 public new event EventHandler BackgroundImageLayoutChanged {
123 add { base.BackgroundImageLayoutChanged += value; }
124 remove { base.BackgroundImageLayoutChanged -= value; }
128 [EditorBrowsable (EditorBrowsableState.Never)]
129 public new event EventHandler Click {
130 add { base.Click += value; }
131 remove { base.Click -= value; }
135 [EditorBrowsable (EditorBrowsableState.Never)]
136 public new event EventHandler DoubleClick {
137 add { base.DoubleClick += value; }
138 remove { base.DoubleClick -= value; }
142 [EditorBrowsable (EditorBrowsableState.Never)]
143 public new event EventHandler FontChanged {
144 add { base.FontChanged += value; }
145 remove { base.FontChanged -= value; }
149 [EditorBrowsable (EditorBrowsableState.Never)]
150 public new event EventHandler ForeColorChanged {
151 add { base.ForeColorChanged += value; }
152 remove { base.ForeColorChanged -= value; }
156 [EditorBrowsable (EditorBrowsableState.Never)]
157 public new event EventHandler ImeModeChanged {
158 add { base.ImeModeChanged += value; }
159 remove { base.ImeModeChanged -= value; }
163 [EditorBrowsable (EditorBrowsableState.Never)]
164 public new event MouseEventHandler MouseClick {
165 add { base.MouseClick += value; }
166 remove { base.MouseClick -= value; }
170 [EditorBrowsable (EditorBrowsableState.Never)]
171 public new event MouseEventHandler MouseDoubleClick {
172 add { base.MouseDoubleClick += value; }
173 remove { base.MouseDoubleClick -= value; }
177 [EditorBrowsable (EditorBrowsableState.Never)]
178 public new event MouseEventHandler MouseDown {
179 add { base.MouseDown += value; }
180 remove { base.MouseDown -= value; }
184 [EditorBrowsable (EditorBrowsableState.Never)]
185 public new event MouseEventHandler MouseMove {
186 add { base.MouseMove += value; }
187 remove { base.MouseMove -= value; }
191 [EditorBrowsable (EditorBrowsableState.Never)]
192 public new event MouseEventHandler MouseUp {
193 add { base.MouseUp += value; }
194 remove { base.MouseUp -= value; }
198 [EditorBrowsable (EditorBrowsableState.Never)]
199 public new event PaintEventHandler Paint {
200 add { base.Paint += value; }
201 remove { base.Paint -= value; }
204 static object ScrollEvent = new object ();
205 static object ValueChangedEvent = new object ();
207 public event ScrollEventHandler Scroll {
208 add { Events.AddHandler (ScrollEvent, value); }
209 remove { Events.RemoveHandler (ScrollEvent, value); }
213 [EditorBrowsable (EditorBrowsableState.Never)]
214 public new event EventHandler TextChanged {
215 add { base.TextChanged += value; }
216 remove { base.TextChanged -= value; }
219 public event EventHandler ValueChanged {
220 add { Events.AddHandler (ValueChangedEvent, value); }
221 remove { Events.RemoveHandler (ValueChangedEvent, value); }
233 timer.Tick += new EventHandler (OnTimer);
234 MouseEnter += new EventHandler (OnMouseEnter);
235 MouseLeave += new EventHandler (OnMouseLeave);
236 base.KeyDown += new KeyEventHandler (OnKeyDownSB);
237 base.MouseDown += new MouseEventHandler (OnMouseDownSB);
238 base.MouseUp += new MouseEventHandler (OnMouseUpSB);
239 base.MouseMove += new MouseEventHandler (OnMouseMoveSB);
240 base.Resize += new EventHandler (OnResizeSB);
241 base.TabStop = false;
242 base.Cursor = Cursors.Default;
244 SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick | ControlStyles.UseTextForAccessibility, false);
247 #region Internal & Private Properties
248 internal Rectangle FirstArrowArea {
250 return this.first_arrow_area;
254 this.first_arrow_area = value;
258 internal Rectangle SecondArrowArea {
260 return this.second_arrow_area;
264 this.second_arrow_area = value;
270 return use_manual_thumb_size ? maximum - manual_thumb_size + 1 :
271 maximum - LargeChange + 1;
275 internal Rectangle ThumbPos {
285 internal bool FirstButtonEntered {
286 get { return first_button_entered; }
288 if (first_button_entered == value)
290 first_button_entered = value;
291 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
292 Invalidate (first_arrow_area);
296 internal bool SecondButtonEntered {
297 get { return second_button_entered; }
299 if (second_button_entered == value)
301 second_button_entered = value;
302 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
303 Invalidate (second_arrow_area);
307 internal bool ThumbEntered {
308 get { return thumb_entered; }
310 if (thumb_entered == value)
312 thumb_entered = value;
313 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
314 Invalidate (thumb_pos);
318 internal bool ThumbPressed {
319 get { return thumb_pressed; }
321 if (thumb_pressed == value)
323 thumb_pressed = value;
324 if (ThemeEngine.Current.ScrollBarHasPressedThumbStyle)
325 Invalidate (thumb_pos);
329 #endregion // Internal & Private Properties
331 #region Public Properties
332 [EditorBrowsable (EditorBrowsableState.Never)]
334 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
335 public override bool AutoSize {
336 get { return base.AutoSize; }
337 set { base.AutoSize = value; }
340 [EditorBrowsable (EditorBrowsableState.Never)]
342 public override Color BackColor
344 get { return base.BackColor; }
346 if (base.BackColor == value)
348 base.BackColor = value;
353 [EditorBrowsable (EditorBrowsableState.Never)]
355 public override Image BackgroundImage
357 get { return base.BackgroundImage; }
359 if (base.BackgroundImage == value)
362 base.BackgroundImage = value;
366 [EditorBrowsable (EditorBrowsableState.Never)]
368 public override ImageLayout BackgroundImageLayout {
369 get { return base.BackgroundImageLayout; }
370 set { base.BackgroundImageLayout = value; }
373 protected override CreateParams CreateParams
375 get { return base.CreateParams; }
378 protected override Padding DefaultMargin {
379 get { return Padding.Empty; }
382 protected override ImeMode DefaultImeMode
384 get { return ImeMode.Disable; }
387 [EditorBrowsable (EditorBrowsableState.Never)]
389 public override Font Font
391 get { return base.Font; }
393 if (base.Font.Equals (value))
400 [EditorBrowsable (EditorBrowsableState.Never)]
402 public override Color ForeColor
404 get { return base.ForeColor; }
406 if (base.ForeColor == value)
409 base.ForeColor = value;
414 [EditorBrowsable (EditorBrowsableState.Never)]
416 public new ImeMode ImeMode
418 get { return base.ImeMode; }
420 if (base.ImeMode == value)
423 base.ImeMode = value;
428 [RefreshProperties(RefreshProperties.Repaint)]
429 [MWFDescription("Scroll amount when clicking in the scroll area"), MWFCategory("Behaviour")]
430 public int LargeChange {
431 get { return Math.Min (large_change, maximum - minimum + 1); }
434 throw new ArgumentOutOfRangeException ("LargeChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
436 if (large_change != value) {
437 large_change = value;
439 // thumb area depends on large change value,
440 // so we need to recalculate it.
442 UpdatePos (Value, true);
445 // UIA Framework: Generate UIA Event to indicate LargeChange change
446 OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.LargeIncrement, value));
452 [RefreshProperties(RefreshProperties.Repaint)]
453 [MWFDescription("Highest value for scrollbar"), MWFCategory("Behaviour")]
455 get { return maximum; }
457 if (maximum == value)
462 // UIA Framework: Generate UIA Event to indicate Maximum change
463 OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.Last, value));
465 if (maximum < minimum)
470 // thumb area depends on maximum value,
471 // so we need to recalculate it.
473 UpdatePos (Value, true);
478 internal void SetValues (int maximum, int large_change)
480 SetValues (-1, maximum, -1, large_change);
483 internal void SetValues (int minimum, int maximum, int small_change, int large_change)
487 if (-1 != minimum && this.minimum != minimum) {
488 this.minimum = minimum;
490 if (minimum > this.maximum)
491 this.maximum = minimum;
494 // change the position if it is out of range now
495 position = Math.Max (position, minimum);
498 if (-1 != maximum && this.maximum != maximum) {
499 this.maximum = maximum;
501 if (maximum < this.minimum)
502 this.minimum = maximum;
505 // change the position if it is out of range now
506 position = Math.Min (position, maximum);
509 if (-1 != small_change && this.small_change != small_change) {
510 this.small_change = small_change;
513 if (this.large_change != large_change) {
514 this.large_change = large_change;
520 UpdatePos (Value, true);
526 [RefreshProperties(RefreshProperties.Repaint)]
527 [MWFDescription("Smallest value for scrollbar"), MWFCategory("Behaviour")]
529 get { return minimum; }
531 if (minimum == value)
536 // UIA Framework: Generate UIA Event to indicate Minimum change
537 OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.First, value));
539 if (minimum > maximum)
542 // thumb area depends on minimum value,
543 // so we need to recalculate it.
545 UpdatePos (Value, true);
551 [MWFDescription("Scroll amount when clicking scroll arrows"), MWFCategory("Behaviour")]
552 public int SmallChange {
553 get { return small_change > LargeChange ? LargeChange : small_change; }
556 throw new ArgumentOutOfRangeException ("SmallChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
558 if (small_change != value) {
559 small_change = value;
560 UpdatePos (Value, true);
563 // UIA Framework: Generate UIA Event to indicate SmallChange change
564 OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.SmallIncrement, value));
569 [DefaultValue (false)]
570 public new bool TabStop {
571 get { return base.TabStop; }
572 set { base.TabStop = value; }
575 [EditorBrowsable (EditorBrowsableState.Never)]
578 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
579 public override string Text {
580 get { return base.Text; }
581 set { base.Text = value; }
586 [MWFDescription("Current value for scrollbar"), MWFCategory("Behaviour")]
588 get { return position; }
590 if ( value < minimum || value > maximum )
591 throw new ArgumentOutOfRangeException ("Value", string.Format ("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
593 if (position != value){
596 OnValueChanged (EventArgs.Empty);
598 if (IsHandleCreated) {
599 Rectangle thumb_rect = thumb_pos;
601 UpdateThumbPos ((vert ? thumb_area.Y : thumb_area.X) + (int)(((float)(position - minimum)) * pixel_per_pos), false, false);
603 MoveThumb (thumb_rect, vert ? thumb_pos.Y : thumb_pos.X);
609 #endregion //Public Properties
611 #region Public Methods
612 protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
614 // Basically, we want to keep our small edge and scale the long edge
615 // ie: if we are vertical, don't scale our width
617 return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Height) | (specified & BoundsSpecified.Location));
619 return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Width) | (specified & BoundsSpecified.Location));
622 protected override void OnEnabledChanged (EventArgs e)
624 base.OnEnabledChanged (e);
627 firstbutton_state = secondbutton_state = ButtonState.Normal;
629 firstbutton_state = secondbutton_state = ButtonState.Inactive;
634 protected override void OnHandleCreated (System.EventArgs e)
636 base.OnHandleCreated (e);
640 UpdateThumbPos (thumb_area.Y + (int)(((float)(position - minimum)) * pixel_per_pos), true, false);
643 protected virtual void OnScroll (ScrollEventArgs se)
645 ScrollEventHandler eh = (ScrollEventHandler)(Events [ScrollEvent]);
649 if (se.NewValue < Minimum) {
650 se.NewValue = Minimum;
653 if (se.NewValue > Maximum) {
654 se.NewValue = Maximum;
660 private void SendWMScroll(ScrollBarCommands cmd) {
661 if ((Parent != null) && Parent.IsHandleCreated) {
663 XplatUI.SendMessage(Parent.Handle, Msg.WM_VSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
665 XplatUI.SendMessage(Parent.Handle, Msg.WM_HSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
670 protected virtual void OnValueChanged (EventArgs e)
672 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
677 public override string ToString()
679 return string.Format("{0}, Minimum: {1}, Maximum: {2}, Value: {3}",
680 GetType( ).FullName, minimum, maximum, position);
683 protected void UpdateScrollInfo ()
688 protected override void WndProc (ref Message m)
690 base.WndProc (ref m);
693 #endregion //Public Methods
695 #region Private Methods
697 private void CalcButtonSizes ()
700 if (Height < ThemeEngine.Current.ScrollBarButtonSize * 2)
701 scrollbutton_height = Height /2;
703 scrollbutton_height = ThemeEngine.Current.ScrollBarButtonSize;
706 if (Width < ThemeEngine.Current.ScrollBarButtonSize * 2)
707 scrollbutton_width = Width /2;
709 scrollbutton_width = ThemeEngine.Current.ScrollBarButtonSize;
713 private void CalcThumbArea ()
715 int lchange = use_manual_thumb_size ? manual_thumb_size : LargeChange;
720 thumb_area.Height = Height - scrollbutton_height - scrollbutton_height;
722 thumb_area.Y = scrollbutton_height;
723 thumb_area.Width = Width;
725 if (Height < thumb_notshown_size)
728 double per = ((double) lchange / (double)((1 + maximum - minimum)));
729 thumb_size = 1 + (int) (thumb_area.Height * per);
731 if (thumb_size < thumb_min_size)
732 thumb_size = thumb_min_size;
734 // Give the user something to drag if LargeChange is zero
735 if (LargeChange == 0)
739 pixel_per_pos = ((float)(thumb_area.Height - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
744 thumb_area.X = scrollbutton_width;
745 thumb_area.Height = Height;
746 thumb_area.Width = Width - scrollbutton_width - scrollbutton_width;
748 if (Width < thumb_notshown_size)
751 double per = ((double) lchange / (double)((1 + maximum - minimum)));
752 thumb_size = 1 + (int) (thumb_area.Width * per);
754 if (thumb_size < thumb_min_size)
755 thumb_size = thumb_min_size;
757 // Give the user something to drag if LargeChange is zero
758 if (LargeChange == 0)
762 pixel_per_pos = ((float)(thumb_area.Width - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
766 private void LargeIncrement ()
768 ScrollEventArgs event_args;
769 int pos = Math.Min (MaximumAllowed, position + large_change);
771 event_args = new ScrollEventArgs (ScrollEventType.LargeIncrement, pos);
772 OnScroll (event_args);
773 Value = event_args.NewValue;
775 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
776 OnScroll (event_args);
777 Value = event_args.NewValue;
779 // UIA Framework event invoked when the "LargeIncrement
780 // Button" is "clicked" either by using the Invoke Pattern
781 // or the space between the thumb and the bottom/right button
782 OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeIncrement, Value));
785 private void LargeDecrement ()
787 ScrollEventArgs event_args;
788 int pos = Math.Max (Minimum, position - large_change);
790 event_args = new ScrollEventArgs (ScrollEventType.LargeDecrement, pos);
791 OnScroll (event_args);
792 Value = event_args.NewValue;
794 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
795 OnScroll (event_args);
796 Value = event_args.NewValue;
798 // UIA Framework event invoked when the "LargeDecrement
799 // Button" is "clicked" either by using the Invoke Pattern
800 // or the space between the thumb and the top/left button
801 OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeDecrement, Value));
804 private void OnResizeSB (Object o, EventArgs e)
806 if (Width <= 0 || Height <= 0)
811 UpdatePos (position, true);
816 internal override void OnPaintInternal (PaintEventArgs pevent)
818 ThemeEngine.Current.DrawScrollBar (pevent.Graphics, pevent.ClipRectangle, this);
821 private void OnTimer (Object source, EventArgs e)
825 switch (timer_type) {
827 case TimerType.HoldButton:
828 SetRepeatButtonTimer ();
831 case TimerType.RepeatButton:
833 if ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Minimum) {
835 SendWMScroll(ScrollBarCommands.SB_LINEUP);
838 if ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Maximum) {
840 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
846 case TimerType.HoldThumbArea:
847 SetRepeatThumbAreaTimer ();
850 case TimerType.RepeatThumbArea:
852 Point pnt, pnt_screen;
853 Rectangle thumb_area_screen = thumb_area;
855 pnt_screen = PointToScreen (new Point (thumb_area.X, thumb_area.Y));
856 thumb_area_screen.X = pnt_screen.X;
857 thumb_area_screen.Y = pnt_screen.Y;
859 if (thumb_area_screen.Contains (MousePosition) == false) {
860 timer.Enabled = false;
861 thumb_moving = ThumbMoving.None;
866 pnt = PointToClient (MousePosition);
869 lastclick_pos = pnt.Y;
871 lastclick_pos = pnt.X;
873 if (thumb_moving == ThumbMoving.Forward) {
874 if ((vert && (thumb_pos.Y + thumb_size > lastclick_pos)) ||
875 (!vert && (thumb_pos.X + thumb_size > lastclick_pos)) ||
876 (thumb_area.Contains (pnt) == false)) {
877 timer.Enabled = false;
878 thumb_moving = ThumbMoving.None;
883 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
886 if ((vert && (thumb_pos.Y < lastclick_pos)) ||
887 (!vert && (thumb_pos.X < lastclick_pos))){
888 timer.Enabled = false;
889 thumb_moving = ThumbMoving.None;
890 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
894 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
907 private void MoveThumb (Rectangle original_thumbpos, int value)
909 /* so, the reason this works can best be
910 * described by the following 1 dimensional
913 * say you have a scrollbar thumb positioned
916 * <---------------------| |------------------------------>
918 * and you want it to end up looking like this:
920 * <-----------------------------| |---------------------->
922 * that can be done with the scrolling api by
923 * extending the rectangle to encompass both
926 * start of range end of range
928 * <---------------------| |-------|---------------------->
930 * so, we end up scrolling just this little region:
934 * and end up with ********| |
936 * where ****** is space that is automatically
939 * It's clear that in both cases (left to
940 * right, right to left) we need to extend the
941 * size of the scroll rectangle to encompass
942 * both. In the right to left case, we also
943 * need to decrement the X coordinate.
945 * We call Update after scrolling to make sure
946 * there's no garbage left in the window to be
947 * copied again if we're called before the
948 * paint events have been handled.
954 delta = value - original_thumbpos.Y;
957 original_thumbpos.Y += delta;
958 original_thumbpos.Height -= delta;
961 original_thumbpos.Height += delta;
964 XplatUI.ScrollWindow (Handle, original_thumbpos, 0, delta, false);
967 delta = value - original_thumbpos.X;
970 original_thumbpos.X += delta;
971 original_thumbpos.Width -= delta;
974 original_thumbpos.Width += delta;
977 XplatUI.ScrollWindow (Handle, original_thumbpos, delta, 0, false);
983 private void OnMouseMoveSB (object sender, MouseEventArgs e)
985 if (Enabled == false)
988 FirstButtonEntered = first_arrow_area.Contains (e.Location);
989 SecondButtonEntered = second_arrow_area.Contains (e.Location);
994 ThumbEntered = thumb_pos.Contains (e.Location);
996 if (firstbutton_pressed) {
997 if (!first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
998 firstbutton_state = ButtonState.Normal;
999 Invalidate (first_arrow_area);
1002 } else if (first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
1003 firstbutton_state = ButtonState.Pushed;
1004 Invalidate (first_arrow_area);
1008 } else if (secondbutton_pressed) {
1009 if (!second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
1010 secondbutton_state = ButtonState.Normal;
1011 Invalidate (second_arrow_area);
1014 } else if (second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
1015 secondbutton_state = ButtonState.Pushed;
1016 Invalidate (second_arrow_area);
1020 } else if (thumb_pressed == true) {
1022 int thumb_edge = e.Y - thumbclick_offset;
1024 if (thumb_edge < thumb_area.Y)
1025 thumb_edge = thumb_area.Y;
1026 else if (thumb_edge > thumb_area.Bottom - thumb_size)
1027 thumb_edge = thumb_area.Bottom - thumb_size;
1029 if (thumb_edge != thumb_pos.Y) {
1030 Rectangle thumb_rect = thumb_pos;
1032 UpdateThumbPos (thumb_edge, false, true);
1034 MoveThumb (thumb_rect, thumb_pos.Y);
1036 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
1038 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1040 int thumb_edge = e.X - thumbclick_offset;
1042 if (thumb_edge < thumb_area.X)
1043 thumb_edge = thumb_area.X;
1044 else if (thumb_edge > thumb_area.Right - thumb_size)
1045 thumb_edge = thumb_area.Right - thumb_size;
1047 if (thumb_edge != thumb_pos.X) {
1048 Rectangle thumb_rect = thumb_pos;
1050 UpdateThumbPos (thumb_edge, false, true);
1052 MoveThumb (thumb_rect, thumb_pos.X);
1054 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
1056 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1063 private void OnMouseDownSB (object sender, MouseEventArgs e)
1067 if (Enabled == false || (e.Button & MouseButtons.Left) == 0)
1070 if (firstbutton_state != ButtonState.Inactive && first_arrow_area.Contains (e.X, e.Y)) {
1071 SendWMScroll(ScrollBarCommands.SB_LINEUP);
1072 firstbutton_state = ButtonState.Pushed;
1073 firstbutton_pressed = true;
1074 Invalidate (first_arrow_area);
1076 if (!timer.Enabled) {
1077 SetHoldButtonClickTimer ();
1078 timer.Enabled = true;
1082 if (secondbutton_state != ButtonState.Inactive && second_arrow_area.Contains (e.X, e.Y)) {
1083 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
1084 secondbutton_state = ButtonState.Pushed;
1085 secondbutton_pressed = true;
1086 Invalidate (second_arrow_area);
1088 if (!timer.Enabled) {
1089 SetHoldButtonClickTimer ();
1090 timer.Enabled = true;
1094 if (thumb_size > 0 && thumb_pos.Contains (e.X, e.Y)) {
1095 ThumbPressed = true;
1096 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1098 thumbclick_offset = e.Y - thumb_pos.Y;
1099 lastclick_pos = e.Y;
1102 thumbclick_offset = e.X - thumb_pos.X;
1103 lastclick_pos = e.X;
1106 if (thumb_size > 0 && thumb_area.Contains (e.X, e.Y)) {
1109 lastclick_pos = e.Y;
1111 if (e.Y > thumb_pos.Y + thumb_pos.Height) {
1112 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
1114 thumb_moving = ThumbMoving.Forward;
1115 Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1116 ClientRectangle.Width,
1117 ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
1118 scrollbutton_height));
1120 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
1122 thumb_moving = ThumbMoving.Backwards;
1123 Dirty (new Rectangle (0, scrollbutton_height,
1124 ClientRectangle.Width,
1125 thumb_pos.Y - scrollbutton_height));
1129 lastclick_pos = e.X;
1131 if (e.X > thumb_pos.X + thumb_pos.Width) {
1132 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
1133 thumb_moving = ThumbMoving.Forward;
1135 Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1136 ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
1138 ClientRectangle.Height));
1140 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
1141 thumb_moving = ThumbMoving.Backwards;
1143 Dirty (new Rectangle (scrollbutton_width, 0,
1144 thumb_pos.X - scrollbutton_width,
1145 ClientRectangle.Height));
1149 SetHoldThumbAreaTimer ();
1150 timer.Enabled = true;
1156 private void OnMouseUpSB (object sender, MouseEventArgs e)
1160 if (Enabled == false)
1163 timer.Enabled = false;
1164 if (thumb_moving != ThumbMoving.None) {
1166 thumb_moving = ThumbMoving.None;
1169 if (firstbutton_pressed) {
1170 firstbutton_state = ButtonState.Normal;
1171 if (first_arrow_area.Contains (e.X, e.Y)) {
1174 SendWMScroll(ScrollBarCommands.SB_LINEUP);
1175 firstbutton_pressed = false;
1176 Dirty (first_arrow_area);
1177 } else if (secondbutton_pressed) {
1178 secondbutton_state = ButtonState.Normal;
1179 if (second_arrow_area.Contains (e.X, e.Y)) {
1182 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
1183 Dirty (second_arrow_area);
1184 secondbutton_pressed = false;
1185 } else if (thumb_pressed == true) {
1186 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbPosition, position));
1187 OnScroll (new ScrollEventArgs (ScrollEventType.EndScroll, position));
1188 SendWMScroll(ScrollBarCommands.SB_THUMBPOSITION);
1189 ThumbPressed = false;
1196 private void OnKeyDownSB (Object o, KeyEventArgs key)
1198 if (Enabled == false)
1203 switch (key.KeyCode){
1241 // I hate to do this, but we don't have the resources to track
1242 // down everything internal that is setting a value outside the
1243 // correct range, so we'll clamp it to the acceptable values.
1244 internal void SafeValueSet (int value)
1246 value = Math.Min (value, maximum);
1247 value = Math.Max (value, minimum);
1252 private void SetEndPosition ()
1254 ScrollEventArgs event_args;
1255 int pos = MaximumAllowed;
1257 event_args = new ScrollEventArgs (ScrollEventType.Last, pos);
1258 OnScroll (event_args);
1259 pos = event_args.NewValue;
1261 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1262 OnScroll (event_args);
1263 pos = event_args.NewValue;
1268 private void SetHomePosition ()
1270 ScrollEventArgs event_args;
1273 event_args = new ScrollEventArgs (ScrollEventType.First, pos);
1274 OnScroll (event_args);
1275 pos = event_args.NewValue;
1277 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1278 OnScroll (event_args);
1279 pos = event_args.NewValue;
1284 private void SmallIncrement ()
1286 ScrollEventArgs event_args;
1287 int pos = Math.Min (MaximumAllowed, position + SmallChange);
1289 event_args = new ScrollEventArgs (ScrollEventType.SmallIncrement, pos);
1290 OnScroll (event_args);
1291 Value = event_args.NewValue;
1293 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
1294 OnScroll (event_args);
1295 Value = event_args.NewValue;
1297 // UIA Framework event invoked when the "SmallIncrement
1298 // Button" (a.k.a bottom/right button) is "clicked" either
1299 // by using the Invoke Pattern or the button itself
1300 OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallIncrement, Value));
1303 private void SmallDecrement ()
1305 ScrollEventArgs event_args;
1306 int pos = Math.Max (Minimum, position - SmallChange);
1308 event_args = new ScrollEventArgs (ScrollEventType.SmallDecrement, pos);
1309 OnScroll (event_args);
1310 Value = event_args.NewValue;
1312 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
1313 OnScroll (event_args);
1314 Value = event_args.NewValue;
1316 // UIA Framework event invoked when the "SmallDecrement
1317 // Button" (a.k.a top/left button) is "clicked" either
1318 // by using the Invoke Pattern or the button itself
1319 OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallDecrement, Value));
1322 private void SetHoldButtonClickTimer ()
1324 timer.Enabled = false;
1325 timer.Interval = 200;
1326 timer_type = TimerType.HoldButton;
1327 timer.Enabled = true;
1330 private void SetRepeatButtonTimer ()
1332 timer.Enabled = false;
1333 timer.Interval = 50;
1334 timer_type = TimerType.RepeatButton;
1335 timer.Enabled = true;
1338 private void SetHoldThumbAreaTimer ()
1340 timer.Enabled = false;
1341 timer.Interval = 200;
1342 timer_type = TimerType.HoldThumbArea;
1343 timer.Enabled = true;
1346 private void SetRepeatThumbAreaTimer ()
1348 timer.Enabled = false;
1349 timer.Interval = 50;
1350 timer_type = TimerType.RepeatThumbArea;
1351 timer.Enabled = true;
1354 private void UpdatePos (int newPos, bool update_thumbpos)
1358 if (newPos < minimum)
1361 if (newPos > MaximumAllowed)
1362 pos = MaximumAllowed;
1366 // pos can't be less than minimum or greater than maximum
1372 if (update_thumbpos) {
1374 UpdateThumbPos (thumb_area.Y + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
1376 UpdateThumbPos (thumb_area.X + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
1380 position = pos; // Updates directly the value to avoid thumb pos update
1383 // XXX some reason we don't call OnValueChanged?
1384 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
1386 eh (this, EventArgs.Empty);
1390 private void UpdateThumbPos (int pixel, bool dirty, bool update_value)
1397 if (pixel < thumb_area.Y)
1398 thumb_pos.Y = thumb_area.Y;
1399 else if (pixel > thumb_area.Bottom - thumb_size)
1400 thumb_pos.Y = thumb_area.Bottom - thumb_size;
1402 thumb_pos.Y = pixel;
1405 thumb_pos.Width = ThemeEngine.Current.ScrollBarButtonSize;
1406 thumb_pos.Height = thumb_size;
1407 new_pos = (float) (thumb_pos.Y - thumb_area.Y);
1408 new_pos = new_pos / pixel_per_pos;
1414 if (pixel < thumb_area.X)
1415 thumb_pos.X = thumb_area.X;
1416 else if (pixel > thumb_area.Right - thumb_size)
1417 thumb_pos.X = thumb_area.Right - thumb_size;
1419 thumb_pos.X = pixel;
1422 thumb_pos.Width = thumb_size;
1423 thumb_pos.Height = ThemeEngine.Current.ScrollBarButtonSize;
1424 new_pos = (float) (thumb_pos.X - thumb_area.X);
1425 new_pos = new_pos / pixel_per_pos;
1432 UpdatePos ((int) new_pos + minimum, false);
1435 private void SetValue (int value)
1437 if ( value < minimum || value > maximum )
1438 throw new ArgumentException(
1439 String.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
1441 if (position != value){
1444 OnValueChanged (EventArgs.Empty);
1445 UpdatePos (value, true);
1449 private void ClearDirty ()
1451 dirty = Rectangle.Empty;
1454 private void Dirty (Rectangle r)
1456 if (dirty == Rectangle.Empty) {
1460 dirty = Rectangle.Union (dirty, r);
1463 private void DirtyThumbArea ()
1465 if (thumb_moving == ThumbMoving.Forward) {
1467 Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1468 ClientRectangle.Width,
1469 ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
1470 scrollbutton_height));
1472 Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1473 ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
1475 ClientRectangle.Height));
1477 } else if (thumb_moving == ThumbMoving.Backwards) {
1479 Dirty(new Rectangle (0, scrollbutton_height,
1480 ClientRectangle.Width,
1481 thumb_pos.Y - scrollbutton_height));
1483 Dirty (new Rectangle (scrollbutton_width, 0,
1484 thumb_pos.X - scrollbutton_width,
1485 ClientRectangle.Height));
1490 private void InvalidateDirty ()
1494 dirty = Rectangle.Empty;
1497 void OnMouseEnter (object sender, EventArgs e)
1499 if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
1500 Region region_to_invalidate = new Region (first_arrow_area);
1501 region_to_invalidate.Union (second_arrow_area);
1502 Invalidate (region_to_invalidate);
1506 void OnMouseLeave (object sender, EventArgs e)
1508 Region region_to_invalidate = new Region ();
1509 region_to_invalidate.MakeEmpty ();
1511 if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
1512 region_to_invalidate.Union (first_arrow_area);
1513 region_to_invalidate.Union (second_arrow_area);
1516 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
1517 if (first_button_entered) {
1518 region_to_invalidate.Union (first_arrow_area);
1520 } else if (second_button_entered) {
1521 region_to_invalidate.Union (second_arrow_area);
1524 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
1525 if (thumb_entered) {
1526 region_to_invalidate.Union (thumb_pos);
1529 first_button_entered = false;
1530 second_button_entered = false;
1531 thumb_entered = false;
1533 Invalidate (region_to_invalidate);
1534 region_to_invalidate.Dispose ();
1536 #endregion //Private Methods
1537 protected override void OnMouseWheel (MouseEventArgs e)
1539 base.OnMouseWheel (e);
1542 #region UIA Framework Section: Events, Methods and Properties.
1545 // We are using Reflection to add/remove internal events.
1546 // Class ScrollBarButtonInvokePatternInvokeEvent uses the events.
1548 // Types used to generate UIA InvokedEvent
1549 // * args.Type = ScrollEventType.LargeIncrement. Space between Thumb and bottom/right Button
1550 // * args.Type = ScrollEventType.LargeDecrement. Space between Thumb and top/left Button
1551 // * args.Type = ScrollEventType.SmallIncrement. Small increment UIA Button (bottom/right Button)
1552 // * args.Type = ScrollEventType.SmallDecrement. Small decrement UIA Button (top/left Button)
1553 // Types used to generate RangeValue-related events
1554 // * args.Type = ScrollEventType.LargeIncrement. LargeChange event
1555 // * args.Type = ScrollEventType.Last. Maximum event
1556 // * args.Type = ScrollEventType.First. Minimum event
1557 // * args.Type = ScrollEventType.SmallIncrement. SmallChange event
1558 static object UIAScrollEvent = new object ();
1559 static object UIAValueChangeEvent = new object ();
1561 internal event ScrollEventHandler UIAScroll {
1562 add { Events.AddHandler (UIAScrollEvent, value); }
1563 remove { Events.RemoveHandler (UIAScrollEvent, value); }
1566 internal event ScrollEventHandler UIAValueChanged {
1567 add { Events.AddHandler (UIAValueChangeEvent, value); }
1568 remove { Events.RemoveHandler (UIAValueChangeEvent, value); }
1571 internal void OnUIAScroll (ScrollEventArgs args)
1573 ScrollEventHandler eh = (ScrollEventHandler) Events [UIAScrollEvent];
1578 internal void OnUIAValueChanged (ScrollEventArgs args)
1580 ScrollEventHandler eh = (ScrollEventHandler) Events [UIAValueChangeEvent];
1586 // Wrapper methods used by the Reflection.
1587 // Class ScrollBarButtonInvokeProviderBehavior uses the events.
1589 internal void UIALargeIncrement ()
1594 internal void UIALargeDecrement ()
1599 internal void UIASmallIncrement ()
1604 internal void UIASmallDecrement ()
1609 internal Rectangle UIAThumbArea {
1610 get { return thumb_area; }
1613 internal Rectangle UIAThumbPosition {
1614 get { return thumb_pos; }
1617 #endregion UIA Framework Section: Events, Methods and Properties.