1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004 Novell, Inc.
23 // Peter Bartok pbartok@novell.com
27 using System.ComponentModel;
28 using System.ComponentModel.Design;
30 using System.Runtime.InteropServices;
32 namespace System.Windows.Forms {
33 [Designer ("System.Windows.Forms.Design.ScrollableControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
35 [ClassInterface (ClassInterfaceType.AutoDispatch)]
38 public class ScrollableControl : Control {
39 #region Local Variables
40 private bool force_hscroll_visible;
41 private bool force_vscroll_visible;
42 private bool auto_scroll;
43 private Size auto_scroll_margin;
44 private Size auto_scroll_min_size;
45 private Point scroll_position;
46 private DockPaddingEdges dock_padding;
47 private SizeGrip sizegrip;
48 internal ImplicitHScrollBar hscrollbar;
49 internal ImplicitVScrollBar vscrollbar;
50 internal Size canvas_size;
51 private Rectangle display_rectangle;
52 private Control old_parent;
55 private HScrollProperties horizontalScroll;
56 private VScrollProperties verticalScroll;
58 #endregion // Local Variables
60 [TypeConverter(typeof(ScrollableControl.DockPaddingEdgesConverter))]
61 #region Subclass DockPaddingEdges
62 public class DockPaddingEdges : ICloneable
64 private Control owner;
67 internal DockPaddingEdges (Control owner)
72 #region DockPaddingEdges Public Instance Properties
73 [RefreshProperties (RefreshProperties.All)]
75 get { return owner.Padding.All; }
76 set { owner.Padding = new Padding (value); }
79 [RefreshProperties (RefreshProperties.All)]
81 get { return owner.Padding.Bottom; }
82 set { owner.Padding = new Padding (Left, Top, Right, value); }
85 [RefreshProperties (RefreshProperties.All)]
87 get { return owner.Padding.Left; }
88 set { owner.Padding = new Padding (value, Top, Right, Bottom); }
91 [RefreshProperties (RefreshProperties.All)]
93 get { return owner.Padding.Right; }
94 set { owner.Padding = new Padding (Left, Top, value, Bottom); }
97 [RefreshProperties (RefreshProperties.All)]
99 get { return owner.Padding.Top; }
100 set { owner.Padding = new Padding (Left, value, Right, Bottom); }
102 #endregion // DockPaddingEdges Public Instance Properties
104 // Public Instance Methods
105 public override bool Equals (object other)
107 if (!(other is DockPaddingEdges)) {
111 if ((this.All == ((DockPaddingEdges)other).All) && (this.Left == ((DockPaddingEdges)other).Left) &&
112 (this.Right == ((DockPaddingEdges)other).Right) && (this.Top == ((DockPaddingEdges)other).Top) &&
113 (this.Bottom == ((DockPaddingEdges)other).Bottom)) {
120 public override int GetHashCode ()
122 return All * Top * Bottom * Right * Left;
125 public override string ToString ()
127 return "All = " + All.ToString () + " Top = " + Top.ToString () + " Left = " + Left.ToString () + " Bottom = " + Bottom.ToString () + " Right = " + Right.ToString ();
130 internal void Scale (float dx, float dy)
132 Left = (int)(Left * dx);
133 Right = (int)(Right * dx);
134 Top = (int)(Top * dy);
135 Bottom = (int)(Bottom * dy);
138 object ICloneable.Clone ()
140 return new DockPaddingEdges (owner);
143 #region DockPaddingEdges Local Variables
149 #endregion // DockPaddingEdges Local Variables
151 #region DockPaddingEdges Constructor
152 internal DockPaddingEdges(Control owner) {
160 #endregion // DockPaddingEdges Constructor
162 #region DockPaddingEdges Public Instance Properties
163 [RefreshProperties(RefreshProperties.All)]
176 owner.PerformLayout();
180 [RefreshProperties(RefreshProperties.All)]
190 owner.PerformLayout();
194 [RefreshProperties(RefreshProperties.All)]
204 owner.PerformLayout();
208 [RefreshProperties(RefreshProperties.All)]
218 owner.PerformLayout();
222 [RefreshProperties(RefreshProperties.All)]
232 owner.PerformLayout();
235 #endregion // DockPaddingEdges Public Instance Properties
237 // Public Instance Methods
238 public override bool Equals(object other) {
239 if (! (other is DockPaddingEdges)) {
243 if ( (this.all == ((DockPaddingEdges)other).all) && (this.left == ((DockPaddingEdges)other).left) &&
244 (this.right == ((DockPaddingEdges)other).right) && (this.top == ((DockPaddingEdges)other).top) &&
245 (this.bottom == ((DockPaddingEdges)other).bottom)) {
252 public override int GetHashCode() {
253 return all*top*bottom*right*left;
256 public override string ToString() {
257 return "All = "+all.ToString()+" Top = "+top.ToString()+" Left = "+left.ToString()+" Bottom = "+bottom.ToString()+" Right = "+right.ToString();
260 internal void Scale(float dx, float dy) {
261 left = (int) (left * dx);
262 right = (int) (right * dx);
263 top = (int) (top * dy);
264 bottom = (int) (bottom * dy);
267 object ICloneable.Clone() {
268 DockPaddingEdges padding_edge;
270 padding_edge=new DockPaddingEdges(owner);
272 padding_edge.all=all;
273 padding_edge.left=left;
274 padding_edge.right=right;
275 padding_edge.top=top;
276 padding_edge.bottom=bottom;
282 #endregion // Subclass DockPaddingEdges
284 #region Subclass DockPaddingEdgesConverter
285 public class DockPaddingEdgesConverter : System.ComponentModel.TypeConverter {
286 // Public Constructors
287 public DockPaddingEdgesConverter() {
290 // Public Instance Methods
291 public override PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, Attribute[] attributes) {
292 return TypeDescriptor.GetProperties(typeof(DockPaddingEdges), attributes);
295 public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) {
299 #endregion // Subclass DockPaddingEdgesConverter
301 #region Public Constructors
302 public ScrollableControl() {
303 SetStyle(ControlStyles.ContainerControl, true);
304 SetStyle(ControlStyles.AllPaintingInWmPaint, false);
307 force_hscroll_visible = false;
308 force_vscroll_visible = false;
309 auto_scroll_margin = new Size(0, 0);
310 auto_scroll_min_size = new Size(0, 0);
311 scroll_position = new Point(0, 0);
312 SizeChanged +=new EventHandler(Recalculate);
313 VisibleChanged += new EventHandler (VisibleChangedHandler);
314 LocationChanged += new EventHandler (LocationChangedHandler);
315 ParentChanged += new EventHandler (ParentChangedHandler);
316 HandleCreated += new EventHandler (AddScrollbars);
321 horizontalScroll = new HScrollProperties (this);
322 verticalScroll = new VScrollProperties (this);
326 void VisibleChangedHandler (object sender, EventArgs e)
331 void LocationChangedHandler (object sender, EventArgs e)
333 UpdateSizeGripVisible ();
336 void ParentChangedHandler (object sender, EventArgs e)
339 if (old_parent == Parent)
342 if (old_parent != null) {
343 old_parent.SizeChanged -= new EventHandler (Parent_SizeChanged);
345 old_parent.PaddingChanged -= new EventHandler (Parent_PaddingChanged);
349 if (Parent != null) {
350 Parent.SizeChanged += new EventHandler (Parent_SizeChanged);
352 Parent.PaddingChanged += new EventHandler (Parent_PaddingChanged);
359 void Parent_PaddingChanged (object sender, EventArgs e)
361 UpdateSizeGripVisible ();
364 void Parent_SizeChanged (object sender, EventArgs e)
366 UpdateSizeGripVisible ();
368 #endregion // Public Constructors
370 #region Protected Static Fields
371 protected const int ScrollStateAutoScrolling = 1;
372 protected const int ScrollStateFullDrag = 16;
373 protected const int ScrollStateHScrollVisible = 2;
374 protected const int ScrollStateUserHasScrolled = 8;
375 protected const int ScrollStateVScrollVisible = 4;
376 #endregion // Protected Static Fields
378 #region Public Instance Properties
379 [DefaultValue(false)]
381 [MWFCategory("Layout")]
382 public virtual bool AutoScroll {
388 if (auto_scroll != value) {
390 PerformLayout (this, "AutoScroll");
396 [MWFCategory("Layout")]
397 public Size AutoScrollMargin {
399 return auto_scroll_margin;
403 if (value.Width < 0) {
404 throw new ArgumentException("Width is assigned less than 0", "value.Width");
407 if (value.Height < 0) {
408 throw new ArgumentException("Height is assigned less than 0", "value.Height");
411 auto_scroll_margin = value;
415 internal bool ShouldSerializeAutoScrollMargin ()
417 return this.AutoScrollMargin != new Size (0, 0);
421 [MWFCategory("Layout")]
422 public Size AutoScrollMinSize {
424 return auto_scroll_min_size;
428 if (value != auto_scroll_min_size) {
429 auto_scroll_min_size = value;
431 PerformLayout (this, "AutoScrollMinSize");
436 internal bool ShouldSerializeAutoScrollMinSize ()
438 return this.AutoScrollMinSize != new Size (0, 0);
442 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
443 public Point AutoScrollPosition {
445 return DisplayRectangle.Location;
449 if (value != AutoScrollPosition) {
455 if (hscrollbar.VisibleInternal) {
456 int max = hscrollbar.Maximum - hscrollbar.LargeChange + 1;
457 value.X = value.X < hscrollbar.Minimum ? hscrollbar.Minimum : value.X;
458 value.X = value.X > max ? max : value.X;
459 shift_x = value.X - scroll_position.X;
462 if (vscrollbar.VisibleInternal) {
463 int max = vscrollbar.Maximum - vscrollbar.LargeChange + 1;
464 value.Y = value.Y < vscrollbar.Minimum ? vscrollbar.Minimum : value.Y;
465 value.Y = value.Y > max ? max : value.Y;
466 shift_y = value.Y - scroll_position.Y;
469 ScrollWindow(shift_x, shift_y);
471 if (hscrollbar.VisibleInternal) {
472 if (scroll_position.X >= hscrollbar.Minimum && scroll_position.X <= hscrollbar.Maximum)
473 hscrollbar.Value = scroll_position.X;
476 if (vscrollbar.VisibleInternal) {
477 if (scroll_position.Y >= vscrollbar.Minimum && scroll_position.Y <= vscrollbar.Maximum)
478 vscrollbar.Value = scroll_position.Y;
485 public override Rectangle DisplayRectangle {
491 if (canvas_size.Width <= base.DisplayRectangle.Width) {
492 width = base.DisplayRectangle.Width;
493 if (vscrollbar.VisibleInternal) {
494 width -= vscrollbar.Width;
497 width = canvas_size.Width;
500 if (canvas_size.Height <= base.DisplayRectangle.Height) {
501 height = base.DisplayRectangle.Height;
502 if (hscrollbar.VisibleInternal) {
503 height -= hscrollbar.Height;
506 height = canvas_size.Height;
509 display_rectangle.X = -scroll_position.X;
510 display_rectangle.Y = -scroll_position.Y;
511 display_rectangle.Width = Math.Max(auto_scroll_min_size.Width, width);
512 display_rectangle.Height = Math.Max(auto_scroll_min_size.Height, height);
515 display_rectangle = base.DisplayRectangle;
518 if (dock_padding != null) {
519 display_rectangle.X += dock_padding.Left;
520 display_rectangle.Y += dock_padding.Top;
521 display_rectangle.Width -= dock_padding.Left + dock_padding.Right;
522 display_rectangle.Height -= dock_padding.Top + dock_padding.Bottom;
525 return display_rectangle;
529 [MWFCategory("Layout")]
532 [EditorBrowsable (EditorBrowsableState.Never)]
533 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
535 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
538 public DockPaddingEdges DockPadding {
540 if (dock_padding == null)
541 CreateDockPadding ();
549 [EditorBrowsable (EditorBrowsableState.Always)]
550 public HScrollProperties HorizontalScroll {
551 get { return horizontalScroll; }
555 [EditorBrowsable (EditorBrowsableState.Always)]
556 public VScrollProperties VerticalScroll {
557 get { return verticalScroll; }
560 #endregion // Public Instance Properties
562 #region Protected Instance Methods
563 protected override CreateParams CreateParams {
565 return base.CreateParams;
569 protected bool HScroll {
571 return hscrollbar.VisibleInternal;
575 if (!AutoScroll && hscrollbar.VisibleInternal != value) {
576 force_hscroll_visible = value;
582 protected bool VScroll {
584 return vscrollbar.VisibleInternal;
588 if (!AutoScroll && vscrollbar.VisibleInternal != value) {
589 force_vscroll_visible = value;
594 #endregion // Protected Instance Methods
596 #region Public Instance Methods
597 public void ScrollControlIntoView(Control activeControl) {
601 Rectangle within = new Rectangle ();
602 within.Size = ClientSize;
604 if (!AutoScroll || (!hscrollbar.VisibleInternal && !vscrollbar.VisibleInternal)) {
608 if (!Contains(activeControl)) {
612 if (vscrollbar.Visible) {
613 within.Width -= vscrollbar.Width;
615 if (hscrollbar.Visible) {
616 within.Height -= hscrollbar.Height;
619 // Don't scroll if already visible
620 if (within.Contains (activeControl.Location) && within.Contains (activeControl.Right, activeControl.Bottom)) {
624 // If the control is above the top or the left, move it down and right until it aligns
625 // with the top/left.
626 // If the control is below the bottom or to the right, move it up/left until it aligns
627 // with the bottom/right, but do never move it further than the top/left side.
628 int x_diff = 0, y_diff = 0;
629 if (activeControl.Top <= 0 || activeControl.Height >= within.Height) {
630 y_diff = -activeControl.Top;
631 } else if (activeControl.Bottom > within.Height) {
632 y_diff = within.Height - activeControl.Bottom;
634 if (activeControl.Left <= 0 || activeControl.Width >= within.Width) {
635 x_diff = -activeControl.Left;
636 } else if (activeControl.Right > within.Width) {
637 x_diff = within.Width - activeControl.Right;
639 corner_x = hscrollbar.Value - x_diff;
640 corner_y = vscrollbar.Value - y_diff;
642 if (hscrollbar.VisibleInternal) {
643 if (corner_x > hscrollbar.Maximum) {
644 corner_x = hscrollbar.Maximum;
645 } else if (corner_x < hscrollbar.Minimum) {
646 corner_x = hscrollbar.Minimum;
648 if (corner_x != hscrollbar.Value) {
649 hscrollbar.Value = corner_x;
653 if (vscrollbar.VisibleInternal) {
654 if (corner_y > vscrollbar.Maximum) {
655 corner_y = vscrollbar.Maximum;
656 } else if (corner_y < vscrollbar.Minimum) {
657 corner_y = vscrollbar.Minimum;
659 if (corner_y != vscrollbar.Value) {
660 vscrollbar.Value = corner_y;
665 public void SetAutoScrollMargin(int x, int y) {
674 auto_scroll_margin = new Size(x, y);
677 #endregion // Public Instance Methods
679 #region Protected Instance Methods
680 [EditorBrowsable(EditorBrowsableState.Advanced)]
681 protected virtual void AdjustFormScrollbars(bool displayScrollbars) {
685 [EditorBrowsable(EditorBrowsableState.Advanced)]
686 protected bool GetScrollState(int bit) {
691 [EditorBrowsable(EditorBrowsableState.Advanced)]
692 protected override void OnLayout(LayoutEventArgs levent) {
693 CalculateCanvasSize (true);
695 AdjustFormScrollbars(AutoScroll); // Dunno what the logic is. Passing AutoScroll seems to match MS behaviour
696 base.OnLayout(levent);
699 // The first time through, we just set the canvas to clientsize
700 // so we could re-layout everything according to the flow.
701 // This time we want to actually calculate the canvas.
702 if (this is FlowLayoutPanel) {
703 CalculateCanvasSize (false);
704 AdjustFormScrollbars (AutoScroll);
709 [EditorBrowsable(EditorBrowsableState.Advanced)]
710 protected override void OnMouseWheel(MouseEventArgs e) {
711 if (vscrollbar.VisibleInternal) {
713 if (vscrollbar.Minimum < (vscrollbar.Value - vscrollbar.LargeChange)) {
714 vscrollbar.Value -= vscrollbar.LargeChange;
716 vscrollbar.Value = vscrollbar.Minimum;
719 int maximum_scrollbar_value = vscrollbar.Maximum - vscrollbar.LargeChange + 1;
720 if (maximum_scrollbar_value > (vscrollbar.Value + vscrollbar.LargeChange)) {
721 vscrollbar.Value += vscrollbar.LargeChange;
723 vscrollbar.Value = maximum_scrollbar_value;
727 base.OnMouseWheel(e);
730 [EditorBrowsable(EditorBrowsableState.Advanced)]
731 protected override void OnVisibleChanged(EventArgs e) {
733 UpdateChildrenZOrder ();
734 PerformLayout(this, "Visible");
736 base.OnVisibleChanged(e);
740 [EditorBrowsable (EditorBrowsableState.Never)]
742 protected override void ScaleCore(float dx, float dy) {
743 if (dock_padding != null)
744 dock_padding.Scale(dx, dy);
746 base.ScaleCore(dx, dy);
750 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
752 base.ScaleControl (factor, specified);
755 protected virtual Point ScrollToControl (Control activeControl)
760 Rectangle within = new Rectangle ();
761 within.Size = ClientSize;
763 if (vscrollbar.Visible)
764 within.Width -= vscrollbar.Width;
766 if (hscrollbar.Visible)
767 within.Height -= hscrollbar.Height;
769 // If the control is above the top or the left, move it down and right until it aligns
770 // with the top/left.
771 // If the control is below the bottom or to the right, move it up/left until it aligns
772 // with the bottom/right, but do never move it further than the top/left side.
773 int x_diff = 0, y_diff = 0;
775 if (activeControl.Top <= 0 || activeControl.Height >= within.Height)
776 y_diff = -activeControl.Top;
777 else if (activeControl.Bottom > within.Height)
778 y_diff = within.Height - activeControl.Bottom;
780 if (activeControl.Left <= 0 || activeControl.Width >= within.Width)
781 x_diff = -activeControl.Left;
782 else if (activeControl.Right > within.Width)
783 x_diff = within.Width - activeControl.Right;
785 corner_x = AutoScrollPosition.X + x_diff;
786 corner_y = AutoScrollPosition.Y + y_diff;
788 return new Point (corner_x, corner_y);
792 protected void SetDisplayRectLocation(int x, int y) {
793 // This method is weird. MS documents that the scrollbars are not
794 // updated. We need to move stuff, but leave the scrollbars as is
804 ScrollWindow(scroll_position.X - x , scroll_position.Y - y);
807 protected void SetScrollState(int bit, bool value) {
808 //throw new NotImplementedException();
811 [EditorBrowsable(EditorBrowsableState.Advanced)]
812 protected override void WndProc(ref Message m) {
815 #endregion // Protected Instance Methods
817 #region Internal & Private Methods
818 internal override IntPtr AfterTopMostControl ()
820 // order of scrollbars:
823 // bottom = horizontal
824 if (hscrollbar != null && hscrollbar.Visible)
825 return hscrollbar.Handle;
826 // no need to check for sizegrip since it will only
827 // be visible if hbar is visible.
828 if (vscrollbar != null && vscrollbar.Visible)
829 return hscrollbar.Handle;
831 return base.AfterTopMostControl ();
834 internal virtual void CalculateCanvasSize (bool canOverride) {
842 num_of_children = Controls.Count;
845 extra_width = hscrollbar.Value;
846 extra_height = vscrollbar.Value;
847 if (dock_padding != null) {
848 extra_width += dock_padding.Right;
849 extra_height += dock_padding.Bottom;
852 for (int i = 0; i < num_of_children; i++) {
854 if (child.Dock == DockStyle.Right) {
855 extra_width += child.Width;
856 } else if (child.Dock == DockStyle.Bottom) {
857 extra_height += child.Height;
861 if (!auto_scroll_min_size.IsEmpty) {
862 width = auto_scroll_min_size.Width;
863 height = auto_scroll_min_size.Height;
866 for (int i = 0; i < num_of_children; i++) {
870 case DockStyle.Left: {
871 if ((child.Right + extra_width) > width) {
872 width = child.Right + extra_width;
877 case DockStyle.Top: {
878 if ((child.Bottom + extra_height) > height) {
879 height = child.Bottom + extra_height;
885 case DockStyle.Right:
886 case DockStyle.Bottom: {
893 anchor = child.Anchor;
895 if (((anchor & AnchorStyles.Left) != 0) && ((anchor & AnchorStyles.Right) == 0)) {
896 if ((child.Right + extra_width) > width) {
897 width = child.Right + extra_width;
901 if (((anchor & AnchorStyles.Top) != 0) || ((anchor & AnchorStyles.Bottom) == 0)) {
902 if ((child.Bottom + extra_height) > height) {
903 height = child.Bottom + extra_height;
911 canvas_size.Width = width;
912 canvas_size.Height = height;
915 // Normally DockPadding is created lazyly, as observed in the test cases, but some children
916 // may need to have it always.
917 internal void CreateDockPadding ()
919 if (dock_padding == null)
920 dock_padding = new DockPaddingEdges (this);
923 private void Recalculate (object sender, EventArgs e) {
927 private void Recalculate (bool doLayout) {
928 if (!IsHandleCreated) {
932 Size canvas = canvas_size;
933 Size client = ClientSize;
935 canvas.Width += auto_scroll_margin.Width;
936 canvas.Height += auto_scroll_margin.Height;
938 int right_edge = client.Width;
939 int bottom_edge = client.Height;
941 int prev_bottom_edge;
943 bool hscroll_visible;
944 bool vscroll_visible;
947 prev_right_edge = right_edge;
948 prev_bottom_edge = bottom_edge;
950 if ((force_hscroll_visible || (canvas.Width > right_edge && auto_scroll)) && client.Width > 0) {
951 hscroll_visible = true;
952 bottom_edge = client.Height - SystemInformation.HorizontalScrollBarHeight;
954 hscroll_visible = false;
955 bottom_edge = client.Height;
958 if ((force_vscroll_visible || (canvas.Height > bottom_edge && auto_scroll)) && client.Height > 0) {
959 vscroll_visible = true;
960 right_edge = client.Width - SystemInformation.VerticalScrollBarWidth;
962 vscroll_visible = false;
963 right_edge = client.Width;
966 } while (right_edge != prev_right_edge || bottom_edge != prev_bottom_edge);
968 if (right_edge < 0) right_edge = 0;
969 if (bottom_edge < 0) bottom_edge = 0;
971 Rectangle hscroll_bounds;
972 Rectangle vscroll_bounds;
974 hscroll_bounds = new Rectangle (0, client.Height - SystemInformation.HorizontalScrollBarHeight,
975 ClientRectangle.Width, SystemInformation.HorizontalScrollBarHeight);
976 vscroll_bounds = new Rectangle (client.Width - SystemInformation.VerticalScrollBarWidth, 0,
977 SystemInformation.VerticalScrollBarWidth, ClientRectangle.Height);
979 /* the ScrollWindow calls here are needed
980 * because (this explanation sucks):
982 * when we transition from having a scrollbar to
983 * not having one, we won't receive a scrollbar
984 * moved (value changed) event, so we need to
985 * manually scroll the canvas.
987 * if you can fix this without requiring the
988 * ScrollWindow calls, pdb and toshok will each
992 if (!vscrollbar.Visible) {
993 vscrollbar.Value = 0;
995 if (!hscrollbar.Visible) {
996 hscrollbar.Value = 0;
999 /* Manually setting the size of the thumb should be done before
1000 * the other assignments */
1001 if (hscroll_visible) {
1002 hscrollbar.manual_thumb_size = right_edge;
1003 hscrollbar.LargeChange = right_edge;
1004 hscrollbar.SmallChange = 5;
1005 hscrollbar.Maximum = canvas.Width - 1;
1007 if (hscrollbar != null && hscrollbar.VisibleInternal) {
1008 ScrollWindow (- scroll_position.X, 0);
1010 scroll_position.X = 0;
1013 if (vscroll_visible) {
1014 vscrollbar.manual_thumb_size = bottom_edge;
1015 vscrollbar.LargeChange = bottom_edge;
1016 vscrollbar.SmallChange = 5;
1017 vscrollbar.Maximum = canvas.Height - 1;
1019 if (vscrollbar != null && vscrollbar.VisibleInternal) {
1020 ScrollWindow (0, - scroll_position.Y);
1022 scroll_position.Y = 0;
1025 if (hscroll_visible && vscroll_visible) {
1026 hscroll_bounds.Width -= SystemInformation.VerticalScrollBarWidth;
1027 vscroll_bounds.Height -= SystemInformation.HorizontalScrollBarHeight;
1029 sizegrip.Bounds = new Rectangle (hscroll_bounds.Right,
1030 vscroll_bounds.Bottom,
1031 SystemInformation.VerticalScrollBarWidth,
1032 SystemInformation.HorizontalScrollBarHeight);
1037 hscrollbar.SetBoundsInternal (hscroll_bounds.X, hscroll_bounds.Y, hscroll_bounds.Width, hscroll_bounds.Height, BoundsSpecified.None);
1038 hscrollbar.Visible = hscroll_visible;
1039 if (hscrollbar.Visible)
1040 XplatUI.SetZOrder (hscrollbar.Handle, IntPtr.Zero, true, false);
1042 vscrollbar.SetBoundsInternal (vscroll_bounds.X, vscroll_bounds.Y, vscroll_bounds.Width, vscroll_bounds.Height, BoundsSpecified.None);
1043 vscrollbar.Visible = vscroll_visible;
1044 if (vscrollbar.Visible)
1045 XplatUI.SetZOrder (vscrollbar.Handle, IntPtr.Zero, true, false);
1047 UpdateSizeGripVisible ();
1049 ResumeLayout (doLayout);
1051 // We should now scroll the active control into view,
1052 // the funny part is that ScrollableControl does not have
1053 // the concept of active control.
1054 ContainerControl container = this as ContainerControl;
1055 if (container != null && container.ActiveControl != null) {
1056 ScrollControlIntoView (container.ActiveControl);
1060 internal void UpdateSizeGripVisible ()
1062 if (!IsHandleCreated) {
1066 sizegrip.CapturedControl = Parent;
1067 // This is really wierd, the size grip is only showing up
1068 // if the bottom right corner of the scrollable control is within
1069 // two pixels from the bottom right corner of its parent.
1070 bool show_sizegrip = hscrollbar.VisibleInternal && vscrollbar.VisibleInternal;
1071 bool enable_sizegrip = false;
1072 if (show_sizegrip && Parent != null) {
1073 Point diff = new Point (Parent.ClientRectangle.Bottom - Bottom, Parent.ClientRectangle.Right - Right);
1074 enable_sizegrip = diff.X <= 2 && diff.X >= 0 && diff.Y <= 2 && diff.Y >= 0;
1076 sizegrip.Visible = show_sizegrip;
1077 sizegrip.Enabled = enable_sizegrip || sizegrip.Capture;
1078 if (sizegrip.Visible)
1079 XplatUI.SetZOrder (sizegrip.Handle, vscrollbar.Handle, false, false);
1082 private void HandleScrollBar(object sender, EventArgs e) {
1083 if (sender == vscrollbar) {
1084 if (!vscrollbar.Visible)
1086 ScrollWindow(0, vscrollbar.Value- scroll_position.Y);
1088 if (!hscrollbar.Visible)
1090 ScrollWindow(hscrollbar.Value - scroll_position.X, 0);
1095 private void HandleScrollEvent (object sender, ScrollEventArgs args)
1101 private void AddScrollbars (object o, EventArgs e)
1103 Controls.AddRangeImplicit (new Control[] {hscrollbar, vscrollbar, sizegrip});
1104 HandleCreated -= new EventHandler (AddScrollbars);
1107 private void CreateScrollbars ()
1109 hscrollbar = new ImplicitHScrollBar ();
1110 hscrollbar.Visible = false;
1111 hscrollbar.ValueChanged += new EventHandler (HandleScrollBar);
1112 hscrollbar.Height = SystemInformation.HorizontalScrollBarHeight;
1113 hscrollbar.use_manual_thumb_size = true;
1115 hscrollbar.Scroll += new ScrollEventHandler (HandleScrollEvent);
1118 vscrollbar = new ImplicitVScrollBar ();
1119 vscrollbar.Visible = false;
1120 vscrollbar.ValueChanged += new EventHandler (HandleScrollBar);
1121 vscrollbar.Width = SystemInformation.VerticalScrollBarWidth;
1122 vscrollbar.use_manual_thumb_size = true;
1124 vscrollbar.Scroll += new ScrollEventHandler (HandleScrollEvent);
1127 sizegrip = new SizeGrip (this);
1128 sizegrip.Visible = false;
1131 private void ScrollWindow(int XOffset, int YOffset) {
1132 int num_of_children;
1134 if (XOffset == 0 && YOffset == 0) {
1140 num_of_children = Controls.Count;
1142 for (int i = 0; i < num_of_children; i++) {
1143 Controls[i].Location = new Point (Controls[i].Left - XOffset, Controls[i].Top - YOffset);
1144 //Controls[i].Left -= XOffset;
1145 //Controls[i].Top -= YOffset;
1146 // Is this faster? Controls[i].Location -= new Size(XOffset, YOffset);
1149 scroll_position.X += XOffset;
1150 scroll_position.Y += YOffset;
1152 XplatUI.ScrollWindow (Handle, ClientRectangle, -XOffset, -YOffset, false);
1153 ResumeLayout(false);
1155 #endregion // Internal & Private Methods
1158 static object OnScrollEvent = new object ();
1160 protected virtual void OnScroll (ScrollEventArgs se)
1162 ScrollEventHandler eh = (ScrollEventHandler) (Events [OnScrollEvent]);
1167 protected override void OnPaddingChanged (EventArgs e)
1169 base.OnPaddingChanged (e);
1172 protected override void OnPaintBackground (PaintEventArgs e)
1174 base.OnPaintBackground (e);
1177 [EditorBrowsable (EditorBrowsableState.Advanced)]
1178 protected override void OnRightToLeftChanged (EventArgs e)
1180 base.OnRightToLeftChanged (e);
1183 public event ScrollEventHandler Scroll {
1184 add { Events.AddHandler (OnScrollEvent, value); }
1185 remove { Events.RemoveHandler (OnScrollEvent, value); }