Reputation: 579
I am trying to make a scrollable panel but without the scrollbar and to scroll by dragging with mouse vertically... here is what somebody helped me to do so far.. :
private void panel1_MouseEnter(object sender, EventArgs e)
{
panel1.AutoScroll = false;
}
private int ValidateChange(int change)
{
var padding = -1;
if (change < 0)
{
var max = (from Control control in Controls select control.Top + control.Height + padding).Concat(new[] { int.MinValue }).Max();
if (max < Height + Math.Abs(change))
{
return Height - max;
}
}
else
{
var min = (from Control control in Controls select control.Top).Concat(new[] { int.MaxValue }).Min();
if (min > padding - Math.Abs(change))
{
return padding - min;
}
}
return change;
}
private void HandleDelta(int delta)
{
var change = ValidateChange(delta);
foreach (Control control in Controls)
{
control.Top += change;
}
}
private void panel1_MouseWheel(object sender, MouseEventArgs e)
{
HandleDelta(e.Delta);
base.OnMouseWheel(e);
}
private Point _mouseLastPosition;
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_mouseLastPosition = e.Location;
}
base.OnMouseDown(e);
}
public void panel1_MouseMove(object sender, MouseEventArgs e)
{
if ((MouseButtons & MouseButtons.Left) != 0)
{
var delta = e.Y - _mouseLastPosition.Y;
HandleDelta(delta);
_mouseLastPosition = e.Location;
}
base.OnMouseMove(e);
}
but it goes just too fast..
EDIT: the dragging by mouse looks a little bit weird i dont know why and when I try to use mouse wheel it bugs and gives me this error: An unhandled exception of type 'System.StackOverflowException' occurred in System.Core.dll at this line var max = (from Control control in Controls select control.Top + control.Height + padding).Concat(new[] { int.MinValue }).Max();
Upvotes: 6
Views: 7210
Reputation: 81
I tested all solutions published here, but none of them worked for me.
My situation is that I'm developing a Print Designer in an application for different printouts. This Print Designer offers zooming of the printout and dragging of the print elements on the printout. I wanted to offer dragging the printout itself when pressing the left mouse button on an empty space of the printout.
My form has a Panel (pnlWorkspace) on it with AutoScroll = true. In that I show multiple panels inside of each other
Because of the paper size and zooming the panels may be larger that the containing panel with AutoScroll set to True.
My very simple working solution without any problems (even the Mouse's ScrollWheel still works) uses just a very few lines of code now. First I created a variable directly in the From:
private Boolean dragScroll = false;
Then I added 3 Routines:
private void Panel_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
clickPos = PointToClient(MousePosition);
clickPos.X -= pnlWorkspace.AutoScrollPosition.X;
clickPos.Y -= pnlWorkspace.AutoScrollPosition.Y;
dragScroll = true;
pnlBorder.Cursor = pnlReceipt.Cursor = pnlPrintArea.Cursor = Cursors.SizeAll;
}
}
private void Panel_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button == MouseButtons.Left) && (dragScroll))
{
mousePos = PointToClient(MousePosition);
pnlWorkspace.AutoScrollPosition = new Point((clickPos.X - mousePos.X), (clickPos.Y - mousePos.Y));
}
}
private void Panel_MouseUp(object sender, MouseEventArgs e)
{
dragScroll = false;
pnlBorder.Cursor = pnlReceipt.Cursor = pnlPrintArea.Cursor = Cursors.Default;
}
Then I connected those Routines to the Panels pnlBorder, pnlReceipt, and pnlPrintArea by selecting them in the Properties window. And that's all there is to it :•)
Here's a video showing the Print Designer's functionality: https://youtu.be/frjWn5bOyo8
Upvotes: 0
Reputation: 1946
This works for me. It keeps a padding of 10 pixels on all controls. Try it out and just modify it to your needs.
Let me know if something is missing that you needed.
public class ScrollablePanel : Panel {
private Point _mouseLastPosition;
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
_mouseLastPosition = e.Location;
}
base.OnMouseDown(e);
}
private int ValidateChange(int change) {
var padding = -1;
if (change < 0) {
var max = (from Control control in Controls select control.Top + control.Height + padding).Concat(new[] { int.MinValue }).Max();
if (max < Height + Math.Abs(change)) {
return Height - max;
}
}
else {
var min = (from Control control in Controls select control.Top).Concat(new[] { int.MaxValue }).Min();
if (min > padding - Math.Abs(change)) {
return padding - min;
}
}
return change;
}
private void HandleDelta(int delta) {
var change = ValidateChange(delta);
foreach (Control control in Controls) {
control.Top += change;
}
}
protected override void OnMouseMove(MouseEventArgs e) {
if((MouseButtons & MouseButtons.Left) != 0) {
var delta = e.Y - _mouseLastPosition.Y;
HandleDelta(delta);
_mouseLastPosition = e.Location;
}
base.OnMouseMove(e);
}
protected override void OnMouseWheel(MouseEventArgs e) {
HandleDelta(e.Delta);
base.OnMouseWheel(e);
}
}
Upvotes: 3
Reputation: 54433
The posted code only 'scrolls' or rather moves the Panel horizontally. For a vertical scrolling effect change this:
Panel pa = ss as Panel; pa.Left = pa.Left + ee.X - pPt.X;
to this:
Panel pa = ss as Panel; pa.Top = pa.Top + ee.Y - pPt.Y;
To make any sense the 'scrolled' or 'moved' panel also should be higher than the enclosing form, so change the sizes, maybe like this:
f2.Size = new Size(400, 300);
..
pan.Size = new Size(400, 600);
And the Buttons should be placed vertically as well, so change:
b.Left = (b.Width + 12) * (i - 1);
to
b.Top= (b.Height + 12) * (i - 1);
You also nmade a mistake when copying from the comments: This:
pan.Parent = f2;
should be the last line in the loop. Adding event handlers in the loops means that also the MouseMove
is called 10 times distance of the movement is also tenfold.
Here is what is looks like, when coded correctly:
To make the MouseWheel
work you can code it maybe like this:
pan.MouseWheel += (ss, ee) =>
{
Panel pa = ss as Panel;
pa.Top += ee.Delta > 0 ? 10 : -10;
};
Update Looks like you also want to limit the scrolling so that neither top nor bottom can move into the display area. Here is a check to achieve that; the trick is to first do the check and only then do the moving:
if (ee.Button.HasFlag(MouseButtons.Left))
{
Panel pa = ss as Panel;
int newTop = pa.Top + ee.Y - pPt.Y;
if ((newTop <= pan.Top && newTop + pan.Height > f2.ClientSize.Height) ||
(newTop >= pan.Top && newTop <= 0))
{
if (newTop <= pan.Top && newTop + pan.Height > f2.ClientSize.Height)
newTop = f2.ClientSize.Height - pan.Height;
pa.Top = newTop;
}
}
The MouseWheel
code should be checked and limited in a similar way!
Note: The correct panel height is of the essence here. My original example didn't take care of that, as no scroll limit was asked for. You could get it like this dynamically:
int panHeight = pan.Controls.Cast<Control>().Select(x => x.Bottom).Max();
pan.Height = panHeight; // maybe add a small gap here..?
Upvotes: 6