用鼠标拖动滚动页面

6

我正在尝试制作一个可滚动的面板,但没有滚动条,并通过鼠标垂直拖动滚动...这是某人迄今为止帮助我做到的...:

 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);

    }

但是它太快了...

编辑:用鼠标拖动看起来有点奇怪,我不知道为什么,当我尝试使用鼠标滚轮时,它会出现错误:var max = (from Control control in Controls select control.Top + control.Height + padding).Concat(new[] { int.MinValue }).Max();,报这个错:未处理的类型'System.StackOverflowException'发生在System.Core.dll中。

3个回答

6
已发布的代码仅“滚动”或者更确切地说是水平移动面板。若要实现垂直滚动效果,请更改以下内容:
   Panel pa = ss as Panel; pa.Left = pa.Left + ee.X - pPt.X;

转换为:

   Panel pa = ss as Panel; pa.Top = pa.Top + ee.Y - pPt.Y;

为了让任何意义,'scrolled'或'moved'面板也应该比包围的表单更高,因此请更改尺寸,可能像这样:
    f2.Size = new Size(400, 300);
    ..
    pan.Size = new Size(400, 600);

按钮也应该垂直放置,因此需要更改:

    b.Left = (b.Width + 12) * (i - 1);

to

    b.Top= (b.Height + 12) * (i - 1);

您在复制评论时也出现了错误:应该是这样的:

您还需要注意一点,就是在复制评论时可能会出现错误:

 pan.Parent = f2;

在循环中,应该将其作为最后一行。在循环中添加事件处理程序意味着也会调用MouseMove10次,移动距离也增加了十倍。
下面是正确编码的实现:

enter image description here

如果要使MouseWheel正常工作,可以这样编写:
 pan.MouseWheel += (ss, ee) =>
 {
      Panel pa = ss as Panel;
      pa.Top += ee.Delta > 0 ? 10 : -10;
 };

更新 看起来你也想限制滚动,使得顶部和底部都不能移动到显示区域内。这里是一个实现该效果的方法;关键在于先进行检查,然后再进行移动:

            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;
                }
            }

应该以类似的方式检查和限制MouseWheel代码!

注意:这里正确的面板高度至关重要。我的原始示例没有考虑到这一点,因为没有要求滚动限制。您可以通过以下动态方式获得它:

int panHeight = pan.Controls.Cast<Control>().Select(x => x.Bottom).Max();
pan.Height = panHeight;  // maybe add a small gap here..?

2
我制作了一个视频,并在这里发布,向您展示它的工作原理。这是相同的代码。https://vimeo.com/194139497 - C. Cristi
2
那么,如果我将鼠标移动到循环外面,它就会正常工作了吗? - C. Cristi
2
是的,现在可以运行了....但我已经有一些按钮在我的表单中,我想滚动它们,它也适用于它们吗?此外,我想在顶部和底部设置最大滚动点。 - C. Cristi
2
让我们在聊天中继续这个讨论 - C. Cristi
2
你看到我的消息了吗? - C. Cristi
显示剩余11条评论

3

这对我来说很有效。它在所有控件上保持了10像素的填充。试试看,根据你的需要进行修改。

如果你需要的内容缺失,请告诉我。

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);
    }
}

1
mouseMove和mouse down实际上可以通过鼠标滚轮进行拖动,但是当我使用鼠标滚轮时,它会冻结,之后无法做任何事情... 另外,我将填充更改为-1,因为那是我喜欢的方式... 所以鼠标滚轮不能正常工作。 - C. Cristi
1
仍然无法工作,出现以下错误:在GetMax 中的此行 foreach (Control control in Controls) ,发生了未处理的类型为“System.StackOverflowException”的异常,位于 System.Windows.Forms.dll。 - C. Cristi
1
但是我该怎么以那种方式使用它呢? - C. Cristi
1
哇,你太棒了!但是为了我的信息,你是如何这么快地改变它的? - C. Cristi
2
从Github下载了您的代码,添加了ScrollablePanel类,然后在设计模式下将Panel更改为ScrollablePanel,并从ScrollablePanel代码中删除所有粘贴的代码并设置Dock = Fill。然后测试了代码,可能还进行了一些我现在不记得的更改以确保它能正常工作。如果它能正常工作,请标记为已接受 :) - MrApnea
显示剩余9条评论

0
我测试了这里发布的所有解决方案,但都没有对我起作用。
我的情况是,我正在为不同的打印输出开发一个应用程序中的打印设计师。这个打印设计师提供了打印输出的缩放和拖动打印元素的功能。当在打印输出的空白区域按下鼠标左键时,我想要提供拖动打印输出本身的功能。
我的表单上有一个Panel(pnlWorkspace),其中AutoScroll = true。在其中,我显示了多个相互嵌套的面板。
  1. pnlBorder的BackgroundColor = Black,比纸张尺寸大2像素
  2. pnlReceipt位于pnlBorder内,位置为1, 1,尺寸比pnlBorder小2像素,BackgroundColor = Gainsboro
  3. pnlPrintArea的位置和尺寸根据实际打印机边距设置,BackgroundColor = White
由于纸张尺寸和缩放,面板可能比包含它们的带有AutoScroll设置为True的面板更大。
我的非常简单的工作解决方案没有任何问题(甚至鼠标的滚轮仍然有效),现在只使用了很少的几行代码。首先,我直接在From中创建了一个变量:
    private Boolean dragScroll = false;

然后我添加了3个程序:

    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;
    }

然后我通过在属性窗口中选择它们,将这些例程连接到面板pnlBorder、pnlReceipt和pnlPrintArea。就是这样简单:•)
这里有一个视频展示了打印设计师的功能:https://youtu.be/frjWn5bOyo8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接