Article: WinForms AutoScroll Experiment

Home Page


Consultancy

  • Service Vouchers
  • Escrow Service

Shop



Programming
  • Articles
  • Tools
  • Links

Search

 

Contact

 

PHPinfo


$_SERVER







Panning a scrollable Panel in a Windows Forms

category 'experiment', language C#, created 08-Oct-2011, version V2.0 (10-Oct-2011), by Luc Pattyn


License: The author hereby grants you a worldwide, non-exclusive license to use and redistribute the files and the source code in the article in any way you see fit, provided you keep the copyright notice in place; when code modifications are applied, the notice must reflect that. The author retains copyright to the article, you may not republish or otherwise make available the article, in whole or in part, without the prior written consent of the author.

Disclaimer: This work is provided as is, without any express or implied warranties or conditions or guarantees. You, the user, assume all risk in its use. In no event will the author be liable to you on any legal theory for any special, incidental, consequential, punitive or exemplary damages arising out of this license or the use of the work or otherwise.


The WinForms toolbox offers a number of scrollable Controls, Panel being one of them. This is how it works: the physical size of the Control is specified by its Size property; the logical size is determined as the hull of all the visible Controls it contains. When the logical size exceeds the physical size, i.e. when not all content is visible at once, a scrollable Control starts showing a horizontal and/or a vertical scrollbar assuming AutoScroll is set true; those scrollbars have their MinValue and MaxValue set to the appropriate values automatically. Changing their value within the allowable range would cause the Control to scroll.

In this little experiment, we set out to make the scrollable Panel pan, which means it would also scroll by left-clicking, then moving the cursor around.

The test program

The full test program is available here. It has a main Form, showing two auto-scrolling Panels, as well as a ListBox for logging. This is how it looks:

 

LPAutoScroll1

 

There are two auto-scrolling panels in this experiment, because there could be two very different sets of conditions, and we wanted to try both situations:

  1. The reddish top panel (named "topPanel") holds a number of labels, with a rectangular hull that far exceeds the Size of the panel. With AutoScroll=true that is enough to make the auto-scroll kick in, as the panel isn't able to show all of its content in a limited space. Note that all those labels are disabled, so clicking anywhere inside the top panel actually clicks topPanel itself.
  2. The greenish bottom panel (unsurprisingly named "bottomPanel") holds only one thing: yet another panel (named "innerPanel") which in turn holds a number of labels. The inner panel is large enough to simply hold these labels, the bottom panel isn't large enough to show its inner panel all at once, so once more auto-scroll will be active assuming AutoScroll=true. Once again all labels are disabled, so clicking anywhere in the visible bottom panel will actually be handled by the innerPanel's event handlers, not by the labels, and certainly not by the bottomPanel itself as that is covered by innerPanel.

And that is where the essential difference lies:

  1. all the content of the top panel is disabled, so you can't "touch" it, all you can touch is the panel itself; it is as if you are watching a painting that is covered by a glass plate; clicking the panel clicks the glass, not the stuff underneath. When you click and pan, the mouse position will move as seen by the top panel, making the content pan a simple matter of adapting the scroll position as much as the mouse has moved.
  2. on the other hand, all the content (i.e. the innerPanel) of the bottom panel is enabled: as a large inner panel is present, you can't really touch the bottom panel, anything you "touch" will be the inner panel (there is no glass plate protecting the painting!). Now if the mouse handlers are such that the inner panel were to track the mouse movement, then there would not be any mouse movement at all, as seen by the inner panel. Therefore, we need another point of origin, one that is independent of innerPanel; candidates are the origin of bottomPanel, the Form, or the screen. An easy solution is to use absolute coordinates, i.e. screen position.

The mouse handlers

The panning code is located in the mouse handlers. For the top panel, we have:

private int clickX;		// coordinates of click point
private int clickY;

private void topPanel_MouseDown(object sender, MouseEventArgs e) {
	clickX=topPanel.HorizontalScroll.Value+e.X;
	clickY=topPanel.VerticalScroll.Value+e.Y;
	...
}

private void topPanel_MouseMove(object sender, MouseEventArgs e) {
	if (e.Button==MouseButtons.Left) {
		setScrollValue(topPanel.HorizontalScroll, clickX-e.X);
		setScrollValue(topPanel.VerticalScroll, clickY-e.Y);
	}
}

The setScrollValue() method merely sets a scrollbar's value, after making sure the value is inside the allowable range [MinValue, MaxValue]. For the bottom panel, it is similar, except for the choice of coordinates:

private void innerPanel_MouseDown(object sender, MouseEventArgs e) {
	clickX=bottomPanel.HorizontalScroll.Value+MousePosition.X;
	clickY=bottomPanel.VerticalScroll.Value+MousePosition.Y;
	...
}

private void innerPanel_MouseMove(object sender, MouseEventArgs e) {
	if (e.Button==MouseButtons.Left) {
		setScrollValue(bottomPanel.HorizontalScroll, clickX-MousePosition.X);
		setScrollValue(bottomPanel.VerticalScroll, clickY-MousePosition.Y);
	}
}

This code uses Control.MousePosition to obtain the mouse position using screen coordinates. An alternative would be using control.PointToScreen(e.Location) where control is either bottomPanel or Form1.

Conclusion and final remarks

Conclusion: it works just fine. Some final remarks:

  • Only visible Controls are accounted for when determining the logical size of the scrollable Container;
  • The current situation is calculated from current values and some values at the moment the mouse was clicked; we don't want to accumulate a lot of differences, as this is error prone, and Windows does not guarantee exactly when and how often the MouseMove events get fired.
  • If you were to design the Panel using Visual Designer, I recommend using two Panels: a full-size one holding all the Controls, its size allows for easy designing; and the smaller one which has the intended physical size. Then simply add the large one as the only child of the smaller one (like innerPanel in bottomPanel).

History

  • Version 1.0 (08-Oct-2011): Original version
  • Version 2.0 (10-Oct-2011): Added the second panel


Perceler

Copyright © 2012, Luc Pattyn

Last Modified 04-May-2025