【问题标题】:how to get smartphone like scrolling for a winforms touchscreen app ( scrolling panel )如何让智能手机像滚动 Winforms 触摸屏应用程序(滚动面板)
【发布时间】:2011-08-05 23:05:02
【问题描述】:

在网上搜索文章后,我想出了一个基于 Winforms 的触摸屏应用程序的设计,该应用程序需要像滚动一样的智能手机。该应用程序本身将在平板笔记本电脑或触摸屏台式机上运行。

  • 我将想要滚动的所有内容都放在面板上。
  • 将自动滚动设置为 true(将显示滚动条)
  • 现在将整个面板放在一个组框内
  • 缩小组合框,直到滚动条被隐藏(视觉上隐藏,不可见 = false)

现在我陷入了有趣的部分。我想我必须处理面板上的 mousedown、mouseup 和 mousemove 来设置自动滚动位置,这样当有人触摸面板并拖动时,它就会产生滚动魔法。请帮助填写以下方法存根中的几行代码。 autoscrollposition 上的msdn doc 非常令人困惑,因为它返回负数,但需要用 abs 设置为正数,什么不是。

Point mouseDownPoint;
Point mouseUpPoint;
Point mouseDragPoint;
 private void myPanel_MouseDown(object sender, MouseEventArgs e)
 {
    this.mouseDownPoint = e.Location;
    Console.WriteLine("Mouse down at {0}", e.location);
 }

 private void myPanel_MouseUp(object sender, MouseEventArgs e)
 {
    this.mouseUpPoint = e.Location;
    this.mouseDownPoint = new Point(); //will set for IsEmpty check
    Console.WriteLine("Mouse Up at {0}", e.location);
 }

 private void myPanel_MouseMove(object sender, MouseEventArgs e)
 {
    Console.WriteLine("Mouse at {0}", e.location);
    if (mouseDownPoint.IsEmpty()) //finger is off the touchscreen
       return;
    myPanel.Autocrollposition = ??
 }

谢谢

//更新 - 下面我有试错工作和测试代码。 (未重构)。如果有人有更优雅的解决方案,请发布。

    Point mouseDownPoint;
    private void innerpanel_MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
            this.mouseDownPoint = e.Location;
    }

    private void innerpanel_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left)
            return;
        if ((mouseDownPoint.X == e.Location.X) && (mouseDownPoint.Y == e.Location.Y))
            return;

        Point currAutoS = innerpanel.AutoScrollPosition;
        if (mouseDownPoint.Y > e.Location.Y)
        {
            //finger slide UP
            if (currAutoS.Y != 0)
                currAutoS.Y = Math.Abs(currAutoS.Y) - 5;
        }
        else if (mouseDownPoint.Y < e.Location.Y)
        {
            //finger slide down
            currAutoS.Y = Math.Abs(currAutoS.Y) + 5;
        }
        else
        {
            currAutoS.Y = Math.Abs(currAutoS.Y);
        }

        if (mouseDownPoint.X > e.Location.X)
        {
            //finger slide left
            if (currAutoS.X != 0)
                currAutoS.X = Math.Abs(currAutoS.X) - 5;
        }
        else if (mouseDownPoint.X < e.Location.X)
        {
            //finger slide right
            currAutoS.X = Math.Abs(currAutoS.X) + 5;
        }
        else
        {
            currAutoS.X = Math.Abs(currAutoS.X);
        }
        innerpanel.AutoScrollPosition = currAutoS;
        mouseDownPoint = e.Location; //IMPORTANT

    }

【问题讨论】:

    标签: c# vb.net user-controls scroll touchscreen


    【解决方案1】:

    作为一个组件:

    public partial class TouchableFlowLayoutPanel : FlowLayoutPanel
    {
        private bool _doTouchScroll;
        private Point _mouseStartPoint = Point.Empty;
        private Point _panelStartPoint = Point.Empty;
    
        /// <summary>
        ///     Initializes a new instance of the <see cref="TouchableFlowLayoutPanel" /> class.
        /// </summary>
        public TouchableFlowLayoutPanel()
        {
            InitializeComponent();
    
            Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown;
            Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove;
            Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp;
        }
    
        /// <summary>
        ///     Initializes a new instance of the <see cref="TouchableFlowLayoutPanel" /> class.
        /// </summary>
        /// <param name="container">The container.</param>
        public TouchableFlowLayoutPanel(IContainer container)
        {
            container.Add(this);
    
            InitializeComponent();
    
            Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown;
            Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove;
            Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp;
        }
    
        /// <summary>
        ///     Handles the MouseFilterDown event of the mouseFilter control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">
        ///     The <see cref="MouseFilterEventArgs" /> instance containing the event data.
        /// </param>
        private void mouseFilter_MouseFilterDown(object sender, MouseFilterEventArgs e)
        {
            if (!_doTouchScroll && e.Button == MouseButtons.Left)
            {
                _mouseStartPoint = new Point(e.X, e.Y);
                _panelStartPoint = new Point(-AutoScrollPosition.X,
                                                     -AutoScrollPosition.Y);
            }
        }
    
        /// <summary>
        ///     Handles the MouseFilterMove event of the mouseFilter control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">
        ///     The <see cref="MouseFilterEventArgs" /> instance containing the event data.
        /// </param>
        private void mouseFilter_MouseFilterMove(object sender, MouseFilterEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (!_mouseStartPoint.Equals(Point.Empty))
                {
                    int dx = (e.X - _mouseStartPoint.X);
                    int dy = (e.Y - _mouseStartPoint.Y);
    
                    if (_doTouchScroll)
                    {
                        AutoScrollPosition = new Point(_panelStartPoint.X - dx,
                                                       _panelStartPoint.Y - dy);
                    }
                    else if (Math.Abs(dx) > 10 || Math.Abs(dy) > 10)
                    {
                        _doTouchScroll = true;
                    }
                }
            }
        }
    
        /// <summary>
        ///     Handles the MouseFilterUp event of the mouseFilter control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">
        ///     The <see cref="MouseFilterEventArgs" /> instance containing the event data.
        /// </param>
        private void mouseFilter_MouseFilterUp(object sender, MouseFilterEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (_doTouchScroll && !AutoScrollPosition.Equals(_panelStartPoint) &&
                    !_panelStartPoint.Equals(Point.Empty))
                {
                    // don't fire Click-Event
                    e.Handled = true;
                }
                _doTouchScroll = false;
                _mouseStartPoint = Point.Empty;
                _panelStartPoint = Point.Empty;
            }
        }
    }
    
    internal class MouseFilter : IMessageFilter
    {
        private const int WM_LBUTTONDOWN = 0x0201;
        private const int WM_LBUTTONUP = 0x0202;
        private const int WM_MOUSEMOVE = 0x0200;
    
        /// <summary>
        ///     Filters a message before sending it
        /// </summary>
        /// <param name="m">The message to be sent.This message can not be changed.</param>
        /// <returns>
        ///     true to filter the message and prevent it from being sent. false to allow the message to be sent to the next filter or control.
        /// </returns>
        public bool PreFilterMessage(ref Message m)
        {
            Point mousePosition = Control.MousePosition;
            var args = new MouseFilterEventArgs(MouseButtons.Left, 0, mousePosition.X, mousePosition.Y, 0);
    
            switch (m.Msg)
            {
                case WM_MOUSEMOVE:
                    if (MouseFilterMove != null)
                    {
                        MouseFilterMove(Control.FromHandle(m.HWnd), args);
                    }
                    break;
    
                case WM_LBUTTONDOWN:
                    if (MouseFilterDown != null)
                    {
                        MouseFilterDown(Control.FromHandle(m.HWnd), args);
                    }
                    break;
    
                case WM_LBUTTONUP:
                    if (MouseFilterUp != null)
                    {
                        MouseFilterUp(Control.FromHandle(m.HWnd), args);
                    }
                    break;
            }
    
            // Always allow message to continue to the next filter control
            return args.Handled;
        }
    
        /// <summary>
        ///     Occurs when [mouse filter up].
        /// </summary>
        public event MouseFilterEventHandler MouseFilterUp;
    
        /// <summary>
        ///     Occurs when [mouse filter down].
        /// </summary>
        public event MouseFilterEventHandler MouseFilterDown;
    
        /// <summary>
        ///     Occurs when [mouse filter move].
        /// </summary>
        public event MouseFilterMoveEventHandler MouseFilterMove;
    }
    
    internal delegate void MouseFilterEventHandler(object sender, MouseFilterEventArgs args);
    
    internal delegate void MouseFilterMoveEventHandler(object sender, MouseFilterEventArgs args);
    
    internal class MouseFilterEventArgs
    {
        /// <summary>
        ///     Initializes a new instance of the <see cref="MouseFilterEventArgs" /> class.
        /// </summary>
        /// <param name="mouseButton">The mouse button.</param>
        /// <param name="clicks">The clicks.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="delta">The delta.</param>
        public MouseFilterEventArgs(MouseButtons mouseButton, int clicks, int x, int y, int delta)
        {
            Button = mouseButton;
            Clicks = clicks;
            X = x;
            Y = y;
            Delta = delta;
            Handled = false;
        }
    
        /// <summary>
        ///     Gets or sets the button.
        /// </summary>
        /// <value>
        ///     The button.
        /// </value>
        public MouseButtons Button { get; set; }
    
        /// <summary>
        ///     Gets or sets a value indicating whether this <see cref="MouseFilterEventArgs" /> is handled.
        /// </summary>
        /// <value>
        ///     <c>true</c> if handled; otherwise, <c>false</c>.
        /// </value>
        public bool Handled { get; set; }
    
        /// <summary>
        ///     Gets or sets the X.
        /// </summary>
        /// <value>
        ///     The X.
        /// </value>
        public int X { get; set; }
    
        /// <summary>
        ///     Gets or sets the Y.
        /// </summary>
        /// <value>
        ///     The Y.
        /// </value>
        public int Y { get; set; }
    
        /// <summary>
        ///     Gets or sets the clicks.
        /// </summary>
        /// <value>
        ///     The clicks.
        /// </value>
        public int Clicks { get; set; }
    
        /// <summary>
        ///     Gets or sets the delta.
        /// </summary>
        /// <value>
        ///     The delta.
        /// </value>
        public int Delta { get; set; }
    }
    
    static class Program
    {
        public static MouseFilter mouseFilter = new MouseFilter();
    
        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.AddMessageFilter(mouseFilter);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
    

    【讨论】:

      【解决方案2】:

      7 年后,但对于任何正在寻找整洁的 winforms 解决方案的人来说:

      using System;
      using System.Drawing;
      using System.Windows.Forms;
      
          /// <summary>
          /// Pass the panel into constructor and the control will be turned into a touch scrollable control.
          /// </summary>
          public class TouchScroll
          {
              private Point mouseDownPoint;
              private Panel parentPanel;
      
              /// <summary>
              /// pass in the panel you would like to be touch scrollable and it will be handled here.
              /// </summary>
              /// <param name="panel">The root panel you need to scroll with</param>
              public TouchScroll(Panel panel)
              {
                  parentPanel = panel;
                  AssignEvents(panel);
              }
      
              private void AssignEvents(Control control)
              {
                  control.MouseDown += MouseDown;
                  control.MouseMove += MouseMove;
                  foreach (Control child in control.Controls)
                      AssignEvents(child);
              }
      
              private void MouseDown(object sender, MouseEventArgs e)
              {
                  if (e.Button == MouseButtons.Left)
                      this.mouseDownPoint = Cursor.Position;
              }
      
              private void MouseMove(object sender, MouseEventArgs e)
              {
                  if (e.Button != MouseButtons.Left)
                      return;
      
                  Point pointDifference = new Point(Cursor.Position.X - mouseDownPoint.X, Cursor.Position.Y - mouseDownPoint.Y);
      
                  if ((mouseDownPoint.X == Cursor.Position.X) && (mouseDownPoint.Y == Cursor.Position.Y))
                      return;
      
                  Point currAutoS = parentPanel.AutoScrollPosition;
                  parentPanel.AutoScrollPosition = new Point(Math.Abs(currAutoS.X) - pointDifference.X, Math.Abs(currAutoS.Y) - pointDifference.Y);
                  mouseDownPoint = Cursor.Position; //IMPORTANT
              }
          }
      }
      

      【讨论】:

      • 这是一个很好的答案,也很整洁。但这仅启用 y 轴滚动,在修改此 Point pointDifference = new Point(Cursor.Position.X - mouseDownPoint.X, Cursor.Position.Y - mouseDownPoint.Y); 以启用 x 轴滚动后,对我来说我需要 x 和 y 滚动,小插件。
      【解决方案3】:

      这是我使用 IMessageFilter 的方式。对于所有正在寻找解决方案的人。 首先你必须实现一个过滤器来监听所有的应用事件:

      internal class MouseFilter : IMessageFilter
      {
          private const int WM_LBUTTONDOWN = 0x0201;
          private const int WM_LBUTTONUP = 0x0202;
          private const int WM_MOUSEMOVE = 0x0200;
      
          /// <summary>
          ///     Filtert eine Meldung, bevor sie gesendet wird.
          /// </summary>
          /// <param name="m">Die zu sendende Meldung. Diese Meldung kann nicht geändert werden.</param>
          /// <returns>
          ///     true, um die Meldung zu filtern und das Senden zu verhindern. false, um das Senden der Meldung bis zum nächsten Filter oder Steuerelement zu ermöglichen.
          /// </returns>
          public bool PreFilterMessage(ref Message m)
          {
              Point mousePosition = Control.MousePosition;
              var args = new MouseFilterEventArgs(MouseButtons.Left, 0, mousePosition.X, mousePosition.Y, 0);
      
              switch (m.Msg)
              {
                  case WM_MOUSEMOVE:
                      if (MouseFilterMove != null)
                      {
                          MouseFilterMove(Control.FromHandle(m.HWnd), args);
                      }
                      break;
      
                  case WM_LBUTTONDOWN:
                      if (MouseFilterDown != null)
                      {
                          MouseFilterDown(Control.FromHandle(m.HWnd), args);
                      }
                      break;
      
                  case WM_LBUTTONUP:
                      if (MouseFilterUp != null)
                      {
                          MouseFilterUp(Control.FromHandle(m.HWnd), args);
                      }
                      break;
              }
      
              // Always allow message to continue to the next filter control
              return args.Handled;
          }
      
          /// <summary>
          ///     Occurs when [mouse filter up].
          /// </summary>
          public event MouseFilterEventHandler MouseFilterUp;
      
          /// <summary>
          ///     Occurs when [mouse filter down].
          /// </summary>
          public event MouseFilterEventHandler MouseFilterDown;
      
          /// <summary>
          ///     Occurs when [mouse filter move].
          /// </summary>
          public event MouseFilterMoveEventHandler MouseFilterMove;
      }
      
      internal delegate void MouseFilterEventHandler(object sender, MouseFilterEventArgs args);
      
      internal delegate void MouseFilterMoveEventHandler(object sender, MouseFilterEventArgs args);
      
      internal class MouseFilterEventArgs
      {
          /// <summary>
          ///     Initializes a new instance of the <see cref="MouseFilterEventArgs" /> class.
          /// </summary>
          /// <param name="mouseButton">The mouse button.</param>
          /// <param name="clicks">The clicks.</param>
          /// <param name="x">The x.</param>
          /// <param name="y">The y.</param>
          /// <param name="delta">The delta.</param>
          public MouseFilterEventArgs(MouseButtons mouseButton, int clicks, int x, int y, int delta)
          {
              Button = mouseButton;
              Clicks = clicks;
              X = x;
              Y = y;
              Delta = delta;
              Handled = false;
          }
      
          /// <summary>
          ///     Gets or sets the button.
          /// </summary>
          /// <value>
          ///     The button.
          /// </value>
          public MouseButtons Button { get; set; }
      
          /// <summary>
          ///     Gets or sets a value indicating whether this <see cref="MouseFilterEventArgs" /> is handled.
          /// </summary>
          /// <value>
          ///     <c>true</c> if handled; otherwise, <c>false</c>.
          /// </value>
          public bool Handled { get; set; }
      
          /// <summary>
          ///     Gets or sets the X.
          /// </summary>
          /// <value>
          ///     The X.
          /// </value>
          public int X { get; set; }
      
          /// <summary>
          ///     Gets or sets the Y.
          /// </summary>
          /// <value>
          ///     The Y.
          /// </value>
          public int Y { get; set; }
      
          /// <summary>
          ///     Gets or sets the clicks.
          /// </summary>
          /// <value>
          ///     The clicks.
          /// </value>
          public int Clicks { get; set; }
      
          /// <summary>
          ///     Gets or sets the delta.
          /// </summary>
          /// <value>
          ///     The delta.
          /// </value>
          public int Delta { get; set; }
      }
      

      然后你必须向你的程序注册这个过滤器:

      static class Program
      {
          public static MouseFilter mouseFilter = new MouseFilter();
      
          /// <summary>
          /// Der Haupteinstiegspunkt für die Anwendung.
          /// </summary>
          [STAThread]
          static void Main()
          {
              Application.AddMessageFilter(mouseFilter);
              Application.EnableVisualStyles();
              Application.SetCompatibleTextRenderingDefault(false);
              Application.Run(new MainForm());
          }
      }
      

      现在您可以监听全局鼠标事件并像这样滚动您的可滚动面板:

      public partial class MainForm : Form
      {
          private bool _doTouchScroll;
          private Point _mouseStartPoint = Point.Empty;
          private Point _yourScrollablePanelStartPoint = Point.Empty;
      
          /// <summary>
          ///     Initializes a new instance of the <see cref="MainForm" /> class.
          /// </summary>
          public MainForm()
          {
              InitializeComponent();
      
              Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown;
              Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove;
              Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp;
          }
      
          /// <summary>
          ///     Handles the MouseFilterDown event of the mudmFilter control.
          /// </summary>
          /// <param name="sender">The source of the event.</param>
          /// <param name="e">
          ///     The <see cref="MouseFilterEventArgs" /> instance containing the event data.
          /// </param>
          private void mouseFilter_MouseFilterDown(object sender, MouseFilterEventArgs e)
          {
              if (!_doTouchScroll && e.Button == MouseButtons.Left)
              {
                  _mouseStartPoint = new Point(e.X, e.Y);
                  _yourScrollablePanelStartPoint = new Point(-yourScrollablePanel.AutoScrollPosition.X,
                                                       -yourScrollablePanel.AutoScrollPosition.Y);
              }
          }
      
          /// <summary>
          ///     Handles the MouseFilterMove event of the mudmFilter control.
          /// </summary>
          /// <param name="sender">The source of the event.</param>
          /// <param name="e">
          ///     The <see cref="MouseFilterEventArgs" /> instance containing the event data.
          /// </param>
          private void mouseFilter_MouseFilterMove(object sender, MouseFilterEventArgs e)
          {
              if (e.Button == MouseButtons.Left)
              {
                  if (!_mouseStartPoint.Equals(Point.Empty))
                  {
                      int dx = (e.X - _mouseStartPoint.X);
                      int dy = (e.Y - _mouseStartPoint.Y);
      
                      if (_doTouchScroll)
                      {
                          yourScrollablePanel.AutoScrollPosition = new Point(_yourScrollablePanelStartPoint.X - dx,
                                                                       _yourScrollablePanelStartPoint.Y - dy);
                      }
                      else if (Math.Abs(dx) > 10 || Math.Abs(dy) > 10)
                      {
                          _doTouchScroll = true;
                      }
                  }
              }
          }
      
          /// <summary>
          ///     Handles the MouseFilterUp event of the mudmFilter control.
          /// </summary>
          /// <param name="sender">The source of the event.</param>
          /// <param name="e">
          ///     The <see cref="MouseFilterEventArgs" /> instance containing the event data.
          /// </param>
          private void mouseFilter_MouseFilterUp(object sender, MouseFilterEventArgs e)
          {
              if (e.Button == MouseButtons.Left)
              {
                  if (_doTouchScroll && !yourScrollablePanel.AutoScrollPosition.Equals(_yourScrollablePanelStartPoint) &&
                      !_yourScrollablePanelStartPoint.Equals(Point.Empty))
                  {
                      // dont fire Click-Event
                      e.Handled = true;
                  }
                  _doTouchScroll = false;
                  _mouseStartPoint = Point.Empty;
                  _yourScrollablePanelStartPoint = Point.Empty;
              }
          }
      }
      

      【讨论】:

      • 很棒的实现,谢谢。我只是好奇我是否错误地实施了这一点。是否期望能够在每个表单中滚动嵌套/多个面板?如果不是,我不是通过硬编码容器来自动滚动来解决这个问题,而是在鼠标按下时,我们会在鼠标下方获得最底部的可滚动控件。比我们用它来滚动直到鼠标向上。当然,如果这只是对实施的误解,我想避免这样做。非常感谢您的分享,我在这里学到了很多。
      【解决方案4】:

      我使用了 OP 发布的代码,但发现如果面板中有标签之类的东西,它就不起作用。为了使这项工作更顺利,我进行了更改。

      e.Location 
      

      PointToClient(Cursor.Position) 
      

      然后让面板内的所有对象也调用 mousedown 和 mousemove 事件。这样无论你点击它应该移动到哪里。

      【讨论】:

        【解决方案5】:

        到原来的帖子,考虑廉价葬礼的答案。

        这个版本更简单,触感更流畅(考虑手指移动速度但不考虑移开手指后仍然滚动的效果,就像移动但加速(也许我有时间时会这样做) )

        适用于面板或 FlowLayoutPanel

        创建一个表单并在其上插入一个 FlowLayoutPanel。

        表格代码:

        Public Class Form1
        
        Private Function GenerateButton(pName As String) As Button
            Dim mResult As New Button
            With mResult
                .Name = pName
                .Text = pName
                .Width = FlowPanel.Width
                .Height = 100
                .Margin = New Padding(0)
                .Padding = New Padding(0)
                .BackColor = Color.CornflowerBlue
                AddHandler .MouseDown, AddressOf Button_MouseDown
                AddHandler .MouseMove, AddressOf Button_MouseMove
            End With
        
            Return mResult
        End Function
        
        
        
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            FlowPanel.Padding = New Padding(0)
            FlowPanel.Margin = New Padding(0)
            Dim i As Integer
            For i = 1 To 100
                FlowPanel.Controls.Add(GenerateButton("btn" & i.ToString))
            Next
        End Sub
        
        Dim myMouseDownPoint As Point
        Dim myCurrAutoSMouseDown As Point
        Private Sub Button_MouseDown(sender As Object, e As MouseEventArgs) Handles FlowPanel.MouseDown
            myMouseDownPoint = PointToClient(Cursor.Position)
            myCurrAutoSMouseDown = FlowPanel.AutoScrollPosition
        End Sub
        
        Private Sub Button_MouseMove(sender As Object, e As MouseEventArgs) Handles FlowPanel.MouseMove
            If e.Button = Windows.Forms.MouseButtons.Left Then
                Dim mLocation As Point = PointToClient(Cursor.Position)
                If myMouseDownPoint <> mLocation Then
                    Dim mCurrAutoS As Point
                    Dim mDeslocation As Point = myMouseDownPoint - mLocation
                    mCurrAutoS.X = Math.Abs(myCurrAutoSMouseDown.X) + mDeslocation.X
                    mCurrAutoS.Y = Math.Abs(myCurrAutoSMouseDown.Y) + mDeslocation.Y
        
                    FlowPanel.AutoScrollPosition = mCurrAutoS
        
                End If
            End If
        End Sub
        

        提示: 要隐藏 flowlayoutpanel(或面板)的滚动,请创建一个继承自 flowlayoutpanel(或面板)的用户控件,然后:

        Imports System.Runtime.InteropServices
        
        Public Class FlowLayoutPanelExt
        Inherits FlowLayoutPanel
        
        
        <DllImport("user32.dll")> _
        Private Shared Function ShowScrollBar(hWnd As IntPtr, wBar As Integer, bShow As Boolean) As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function
        
        Private Enum ScrollBarDirection
            SB_HORZ = 0
            SB_VERT = 1
            SB_CTL = 2
            SB_BOTH = 3
        End Enum
        
        Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
            If Me.Visible Then
                ShowScrollBar(Me.Handle, CInt(ScrollBarDirection.SB_BOTH), False)
                MyBase.WndProc(m)
            End If
        End Sub
        
        Public Sub New()
        
            ' This call is required by the designer.
            InitializeComponent()
        
            ' Add any initialization after the InitializeComponent() call.
            Me.AutoScroll = True
        End Sub
        
        End Class
        

        【讨论】:

          【解决方案6】:

          这个解决方案似乎是最好的解决方案,也是最普遍接受的解决方案 - 但是,如果您滚动到底部并触摸按钮后面的实际流控制(我试图这样做以便有空白空间),然后您必须双击并按住按钮才能恢复滚动。重新启动应用程序会恢复类似手机的滚动功能。我想知道是否有其他人已经看到或弄清楚了 - 用你的应用程序尝试一下,看看是否也是如此。我修改了上面的 sn-p 以便您可以开始一个新项目,将其复制并粘贴到 form1 的代码中,然后点击运行。

             Public Class Form1
                  Dim FlowPanel As New FlowLayoutPanel
                  Private Function GenerateButton(ByVal pName As String) As Button
                      Dim mResult As New Button
                      With mResult
                          .Name = pName
                          .Text = pName
                          .Width = 128
                          .Height = 128
                          .Margin = New Padding(0)
                          .Padding = New Padding(0)
                          .BackColor = Color.CornflowerBlue
                          AddHandler .MouseDown, AddressOf Button_MouseDown
                          AddHandler .MouseMove, AddressOf Button_MouseMove
                      End With
          
                      Return mResult
                  End Function
          
          
          
                  Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
          
                      Me.Width = 806
                      Me.Height = 480
                      FlowPanel.Padding = New Padding(0)
                      FlowPanel.Margin = New Padding(0)
                      ' FlowPanel.ColumnCount = Me.Width / (128 + 6)
                      FlowPanel.Dock = DockStyle.Fill
                      FlowPanel.AutoScroll = True
                      Me.Controls.Add(FlowPanel)
                      Dim i As Integer
                      For i = 1 To 98
                          FlowPanel.Controls.Add(GenerateButton("btn" & i.ToString))
                      Next
                  End Sub
          
                  Dim myMouseDownPoint As Point
                  Dim myCurrAutoSMouseDown As Point
                  Private Sub Button_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
                      myMouseDownPoint = PointToClient(Cursor.Position)
                      myCurrAutoSMouseDown = FlowPanel.AutoScrollPosition
                  End Sub
          
                  Private Sub Button_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
                      If e.Button = Windows.Forms.MouseButtons.Left Then
                          Dim mLocation As Point = PointToClient(Cursor.Position)
                          If myMouseDownPoint <> mLocation Then
                              Dim mCurrAutoS As Point
                              Dim mDeslocation As Point = myMouseDownPoint - mLocation
                              mCurrAutoS.X = Math.Abs(myCurrAutoSMouseDown.X) + mDeslocation.X
                              mCurrAutoS.Y = Math.Abs(myCurrAutoSMouseDown.Y) + mDeslocation.Y
          
                              FlowPanel.AutoScrollPosition = mCurrAutoS
          
                          End If
                      End If
                  End Sub
              End Class
          

          【讨论】:

          • 发布答案不应向访问该站点的任何人提出有关如何解决代码问题的新问题。如果您对代码 sn-p 有疑问,请尝试提问。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-01
          • 1970-01-01
          • 2016-06-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多