繁体   English   中英

如何根据鼠标位置滚动流布局面板内容

[英]How to scroll Flow layout panel content as per mouse position

我已经采用了一个 Flow 布局面板并在其中放置了多个图片框。 现在我想什么时候将鼠标放在流布局面板的最右边或最左边,然后图片的其余部分将滚动出来。 想想 Windows 8 的开始屏幕,屏幕上出现了许多图块,当我们将鼠标放在屏幕的最右边时,其余的图块会滚动出来。 我想用 Flow 布局面板在 windows 窗体中模拟相同的东西。

我希望我的 Flow 布局面板不会显示滚动条,但是当我将鼠标放在面板上的大部分右侧或左侧时,图像会滚动出来。 这是我的屏幕截图在此处输入图像描述

有人告诉我这样做...这是位代码在 Panel 的 MouseMove 事件中设置 AutoScrollPosition 属性。

private void panel1_MouseMove(object sender, MouseEventArgs e)
{
    panel1.AutoScrollPosition = new Point(e.X, e.Y);
}

但是这个技巧并不好。 AutoScrollPosition 在滚动条可见时工作,但在我的情况下,我不想在 Flow 布局面板中显示滚动条。 我想要从左到右或从右到左平滑滚动图像。 任何人都可以帮助我实现我想要做的事情......如果可能的话,指导我进行编码。 谢谢

编辑

在这里,我按照@Taw 的建议修改后给出了我的完整代码,但它不能正常工作....当图片移动时发现闪烁。 无论如何,这是完整的代码。

namespace ScrollTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            flowLayoutPanel1.MouseMove += MouseScroll;

            foreach (Control x in this.Controls)
            {
                if (x is PictureBox)
                {
                    ((PictureBox)x).MouseMove += MouseScroll;         
                }
            }
        }

        int near = 33;
        private void MouseScroll(object sender, MouseEventArgs e)
        {
            Point mouse = flowLayoutPanel1.PointToClient(MousePosition);
            Rectangle C = flowLayoutPanel1.ClientRectangle;

            int dLeft = mouse.X - C.Left;
            int dTop = mouse.Y - C.Top;
            int dRight = C.Right - mouse.X;
            int dBottom = C.Bottom - mouse.Y;

            int dX = dLeft < near ? dLeft : dRight < near ? -dRight : 0;
            int dY = dTop < near ? dTop : dBottom < near ? -dBottom : 0;

            if (dX != 0 | dY != 0) scrollFLP(dX, dY);
        }

        void scrollFLP(int deltaX, int deltaY)
        {
            flowLayoutPanel1.Left += getSpeedFromDistance(deltaX);
            flowLayoutPanel1.Top += getSpeedFromDistance(deltaY);
            System.Threading.Thread.Sleep(11);
        }

        int getSpeedFromDistance(int delta)
        {
            int sig = Math.Sign(delta);
            int d = Math.Abs(delta);
            if (d > near / 2) return sig;
            else if (d > near / 3) return near / 10 * sig;
            else if (d > near / 4) return near / 8 * sig;
            else if (d > near / 5) return near / 5 * sig;
            else return near * sig;
        }
    }
}

基本上我正在尝试实现类似假设我有flow layout panel并且里面有很多图片框,其中有很多图像作为屏幕截图但是滚动条不应该显示,而是当我将鼠标放在顶部或底部时滚动会自动发生流布局面板,如carousel

查看您的应用程序的这张图片在此处输入图像描述

当我的鼠标放在右端时,它会滚动并形成我不想要的背景。 我希望图片框将滚动并滚动到最后一个不超过。

任何想法如何做到这一点。 谢谢

第二次编辑

在此处输入图像描述

我根据您的建议添加了此代码

public Form1()
            {
                InitializeComponent();

                for (int i = 0; i < 666; i++)
                {
                    PictureBox pan = new PictureBox();
                    //pan.MouseMove += MouseScroll;
                    //pan.MouseLeave += outSideCheck;
                    pan.Size = new Size(75, 75);
                    pan.BackColor = Color.FromArgb(255, (i * 2) & 255, (i * 7) & 255, (i * 4) & 255);
                    flowLayoutPanel1.Controls.Add(pan);

                }
                //flowLayoutPanel1.MouseMove += MouseScroll;
                //this.flowLayoutPanel1.MouseLeave += outSideCheck;

                mouseScroller MSC = new mouseScroller();
                MSC.registerControl(flowLayoutPanel1);  // FLP = your FlowLayouPanel
                MSC.timerSpeed = 5;  // optional
                MSC.nearness = 100;  // optional

                flowLayoutPanel1.AutoScroll = false;

            }

现在应用程序在添加新代码后执行有线行为。 如果我犯了任何错误,请指导我。 谢谢

这是一个两部分的问题:

  • 如何抓住事件
  • 如何滚动不可见滚动条的FlowLayoutPanel

第二先。 根据我的发现,这不是一件容易的事,除非您使用一个简单且相当常见的技巧:实际上不要滚动它! 而是将其放入Panel中,然后控制其在该Panel中的位置。

为此,您可以根据需要将Panel panel1添加到 Form、 DockAnchor中,并设置其PictureBox Autoscroll = false (!) . 但我们也不希望Panel显示Scrollbars 。)

将 FLP 设置为所需大小并将其放入面板中,它显然也有Autoscroll = false ,我们已准备好解决设置事件的另一个问题..:

首先,将下面的MouseScroll事件添加到代码中,然后将要使用鼠标移动的每个控件连接到它,即 FLP:

  flowLayoutPanel1.MouseMove += MouseScroll;

..还有你的每个图片框,也许像这样

  // your creation loop..
  PictureBox pbox = new PictureBox();
  pbox.MouseMove += MouseScroll;           // <<--- hook into to the mousemove
  pan.MouseLeave += outSideCheck;          // <<--- hook into to the mouseleave

  // .. do your stuff.. here I put some paint on to test..
  pbox.BackColor = Color.FromArgb(255, 111, (i * 3) & 255, (i * 4) & 255);
  flowLayoutPanel1.Controls.Add(pbox);

或者无论您如何创建它们..

编辑 2我再次更改了我的原始代码。 它现在包括外部检查、向最近边缘移动的检查以及鼠标移动错误的解决方法。 它使用一个设置为 30 毫秒的定时器。 速度映射是其自身的函数。

 flowLayoutPanel1.MouseMove += MouseScroll;
 this.flowLayoutPanel1.MouseLeave += outSideCheck;
 flowLayoutPanel1.AutoScroll = false;


int near = 33;
Point lastLocation = Point.Empty;
int dX = 0;
int dY = 0;

private void MouseScroll(object sender, MouseEventArgs e)
{
    Point  mouse = panel1.PointToClient(MousePosition);
    Rectangle C = panel1.ClientRectangle;
    // mouseMove has a bug, we need to workaround
    if (mouse == lastLocation) return;
    if (lastLocation == Point.Empty) { lastLocation = mouse; return; }


    // distance from each edge
    int dLeft = mouse.X - C.Left;
    int dTop = mouse.Y - C.Top;
    int dRight = C.Right - mouse.X;
    int dBottom = C.Bottom - mouse.Y;

    // relevant distances with sign
        dX = dLeft < near ? dLeft : dRight < near ? -dRight : 0;
        dY = dTop < near ? dTop : dBottom < near ? -dBottom : 0;

        // we need the closest edge to check if we are moving in or out
    List<int> edges = new List<int>() { dLeft, dTop, dRight, dBottom };
    var closest = edges.IndexOf(edges.Min());

    // if we are moving
    if (dX != 0 | dY != 0)
        // if moving out: go else stop going
        if (!movingIn(mouse, closest)) timer1.Start(); else timer1.Stop();
    // remember position    
    lastLocation = mouse;
}

bool movingIn(Point current, int Edge)
{
    switch (Edge)
    {
        case 0:  return current.X > lastLocation.X;
        case 1:  return current.Y > lastLocation.Y;
        case 2:  return current.X < lastLocation.X;
        case 3:  return current.Y < lastLocation.Y;
    }
    return false;
}

void scrollFLP(int deltaX, int deltaY)
{
    flowLayoutPanel1.Left += getSpeedFromDistance(deltaX);
    flowLayoutPanel1.Top += getSpeedFromDistance(deltaY);
    Size C = panel1.ClientSize;
    if (flowLayoutPanel1.Left > 1) { flowLayoutPanel1.Left = 0; timer1.Stop(); }
    if (flowLayoutPanel1.Right < C.Width) 
        { flowLayoutPanel1.Left = C.Width - flowLayoutPanel1.Width; timer1.Stop(); }
    if (flowLayoutPanel1.Top > 1) { flowLayoutPanel1.Top = 0; timer1.Stop(); }
    if (flowLayoutPanel1.Bottom < C.Height) 
        { flowLayoutPanel1.Top = C.Height - flowLayoutPanel1.Height; timer1.Stop(); }
}

int getSpeedFromDistance(int delta)
{
    int sig = Math.Sign(delta); 
    int d = Math.Abs(delta);
    if (d > near / 2) return  sig;
    else if (d > near / 3) return 2 * sig;
    else if (d > near / 4) return 4 * sig;
    else if (d > near / 5) return 6 * sig;
    else return 10 * sig;
}

private void timer1_Tick(object sender, EventArgs e)  
{
        if (insidePanel()) scrollFLP(dX, dY); else timer1.Stop();
    }

bool insidePanel()
{
    return panel1.ClientRectangle.Contains(panel1.PointToClient(MousePosition));
}

private void outSideCheck(object sender, EventArgs e)
{
    if (!insidePanel()) {timer1.Stop(); lastLocation = Point.Empty;}
}

当然你会想玩各种“神奇”的数字:-)

现在包括停止代码和方向检查。

像往常一样,关键是准确地知道你想要什么。我希望这能让你开始实现它!

自从问这个问题以来已经有一段时间了。 我刚遇到这个问题。 我的情况有点不同,但我仍然认为这是解决同一问题的方法(最坏的情况是可以使用计时器控件,因为未打开自动滚动)。

这是我的场景:我有一个panel控件(普通面板)。 我有一个用缩放制作的PictureBox I'm making rectangular selections on top of this image, and when the selections spilled out of the panel, my panel was supposed to slide in the direction I was selecting. (在我的场景中,鼠标被按下)(在我的场景中,自动滚动也是打开的)。 这就是我在不写太多代码的情况下解决它的方法:

我为滚动位置添加了两个私有变量(对整个类范围有效)。

private int xPos;
private int yPos;
private int speed = 5; 

我在加载表单时为它们分配了当前的滚动位置。

private void Form1_Load(object sender, EventArgs e)
{
    //when I change the scrollbar manually or change with zoom I still 
    //need to add these lines to the related event
    xPos = panel1.HorizontalScroll.Value;
    yPos = panel1.VerticalScroll.Value;
}

在我的图片框的 mousemove 事件中

private void picturebox1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left) {

       Point mouse = panel1.PointToClient(MousePosition);

       if (!panel1.ClientRectangle.Contains( mouse ))
       {
           Rectangle CRect = panel1.ClientRectangle;
           int dLeft = mouse.X - CRect.Left;
           int dRight = CRect.Right - mouse.X;
           int dTop = mouse.Y - CRect.Top;
           int dBottom = CRect.Bottom - mouse.Y;
        
           if(dLeft < 0 && panel1.HorizontalScroll.Value > 0)
           {
               xPos = -panel1.AutoScrollPosition.X - speed;
           }
           if (dRight < 0 && panel1.HorizontalScroll.Value < panel1.HorizontalScroll.Maximum)
           {
               xPos = -panel1.AutoScrollPosition.X + speed;
           }
           if (dTop < 0 && panel1.VerticalScroll.Value > 0)
           {
               yPos = -panel1.AutoScrollPosition.Y - speed;
           }
           if (dBottom < 0 && panel1.VerticalScroll.Value < panel1.VerticalScroll.Maximum)
           {
               yPos = -panel1.AutoScrollPosition.Y + speed;
           }
           panel1.AutoScrollPosition = new Point(xPos, yPos);

       }
   }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM