如何让智能手机像滚动winforms触摸屏应用程序(滚动面板)

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

  • 我把我要滚动的所有内容放在面板上。
  • 将autoscroll设置为true(将显示滚动条)
  • 现在将整个面板放在一个组框中
  • 缩小combobox,直到隐藏滚动条(视觉上隐藏,不可见= 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.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 } 

作为一个组件:

 public partial class TouchableFlowLayoutPanel : FlowLayoutPanel { private bool _doTouchScroll; private Point _mouseStartPoint = Point.Empty; private Point _panelStartPoint = Point.Empty; ///  /// Initializes a new instance of the  class. ///  public TouchableFlowLayoutPanel() { InitializeComponent(); Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown; Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove; Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp; } ///  /// Initializes a new instance of the  class. ///  /// The container. public TouchableFlowLayoutPanel(IContainer container) { container.Add(this); InitializeComponent(); Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown; Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove; Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp; } ///  /// Handles the MouseFilterDown event of the mouseFilter control. ///  /// The source of the event. ///  /// The  instance containing the event data. ///  private void mouseFilter_MouseFilterDown(object sender, MouseFilterEventArgs e) { if (!_doTouchScroll && e.Button == MouseButtons.Left) { _mouseStartPoint = new Point(eX, eY); _panelStartPoint = new Point(-AutoScrollPosition.X, -AutoScrollPosition.Y); } } ///  /// Handles the MouseFilterMove event of the mouseFilter control. ///  /// The source of the event. ///  /// The  instance containing the event data. ///  private void mouseFilter_MouseFilterMove(object sender, MouseFilterEventArgs e) { if (e.Button == MouseButtons.Left) { if (!_mouseStartPoint.Equals(Point.Empty)) { int dx = (eX - _mouseStartPoint.X); int dy = (eY - _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; } } } } ///  /// Handles the MouseFilterUp event of the mouseFilter control. ///  /// The source of the event. ///  /// The  instance containing the event data. ///  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; ///  /// Filters a message before sending it ///  /// The message to be sent.This message can not be changed. ///  /// 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. ///  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; } ///  /// Occurs when [mouse filter up]. ///  public event MouseFilterEventHandler MouseFilterUp; ///  /// Occurs when [mouse filter down]. ///  public event MouseFilterEventHandler MouseFilterDown; ///  /// Occurs when [mouse filter move]. ///  public event MouseFilterMoveEventHandler MouseFilterMove; } internal delegate void MouseFilterEventHandler(object sender, MouseFilterEventArgs args); internal delegate void MouseFilterMoveEventHandler(object sender, MouseFilterEventArgs args); internal class MouseFilterEventArgs { ///  /// Initializes a new instance of the  class. ///  /// The mouse button. /// The clicks. /// The x. /// The y. /// The delta. public MouseFilterEventArgs(MouseButtons mouseButton, int clicks, int x, int y, int delta) { Button = mouseButton; Clicks = clicks; X = x; Y = y; Delta = delta; Handled = false; } ///  /// Gets or sets the button. ///  ///  /// The button. ///  public MouseButtons Button { get; set; } ///  /// Gets or sets a value indicating whether this  is handled. ///  ///  /// true if handled; otherwise, false. ///  public bool Handled { get; set; } ///  /// Gets or sets the X. ///  ///  /// The X. ///  public int X { get; set; } ///  /// Gets or sets the Y. ///  ///  /// The Y. ///  public int Y { get; set; } ///  /// Gets or sets the clicks. ///  ///  /// The clicks. ///  public int Clicks { get; set; } ///  /// Gets or sets the delta. ///  ///  /// The delta. ///  public int Delta { get; set; } } static class Program { public static MouseFilter mouseFilter = new MouseFilter(); ///  /// Der Haupteinstiegspunkt für die Anwendung. ///  [STAThread] static void Main() { Application.AddMessageFilter(mouseFilter); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } } 

这是我使用IMessageFilter的方式。 适合所有寻求解决方案的人。 首先,您必须实现一个将侦听所有应用程序事件的filter:

 internal class MouseFilter : IMessageFilter { private const int WM_LBUTTONDOWN = 0x0201; private const int WM_LBUTTONUP = 0x0202; private const int WM_MOUSEMOVE = 0x0200; ///  /// Filtert eine Meldung, bevor sie gesendet wird. ///  /// Die zu sendende Meldung. Diese Meldung kann nicht geändert werden. ///  /// 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. ///  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; } ///  /// Occurs when [mouse filter up]. ///  public event MouseFilterEventHandler MouseFilterUp; ///  /// Occurs when [mouse filter down]. ///  public event MouseFilterEventHandler MouseFilterDown; ///  /// Occurs when [mouse filter move]. ///  public event MouseFilterMoveEventHandler MouseFilterMove; } internal delegate void MouseFilterEventHandler(object sender, MouseFilterEventArgs args); internal delegate void MouseFilterMoveEventHandler(object sender, MouseFilterEventArgs args); internal class MouseFilterEventArgs { ///  /// Initializes a new instance of the  class. ///  /// The mouse button. /// The clicks. /// The x. /// The y. /// The delta. public MouseFilterEventArgs(MouseButtons mouseButton, int clicks, int x, int y, int delta) { Button = mouseButton; Clicks = clicks; X = x; Y = y; Delta = delta; Handled = false; } ///  /// Gets or sets the button. ///  ///  /// The button. ///  public MouseButtons Button { get; set; } ///  /// Gets or sets a value indicating whether this  is handled. ///  ///  /// true if handled; otherwise, false. ///  public bool Handled { get; set; } ///  /// Gets or sets the X. ///  ///  /// The X. ///  public int X { get; set; } ///  /// Gets or sets the Y. ///  ///  /// The Y. ///  public int Y { get; set; } ///  /// Gets or sets the clicks. ///  ///  /// The clicks. ///  public int Clicks { get; set; } ///  /// Gets or sets the delta. ///  ///  /// The delta. ///  public int Delta { get; set; } } 

然后你必须向你注册这个filter程序:

 static class Program { public static MouseFilter mouseFilter = new MouseFilter(); ///  /// Der Haupteinstiegspunkt für die Anwendung. ///  [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; ///  /// Initializes a new instance of the  class. ///  public MainForm() { InitializeComponent(); Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown; Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove; Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp; } ///  /// Handles the MouseFilterDown event of the mudmFilter control. ///  /// The source of the event. ///  /// The  instance containing the event data. ///  private void mouseFilter_MouseFilterDown(object sender, MouseFilterEventArgs e) { if (!_doTouchScroll && e.Button == MouseButtons.Left) { _mouseStartPoint = new Point(eX, eY); _yourScrollablePanelStartPoint = new Point(-yourScrollablePanel.AutoScrollPosition.X, -yourScrollablePanel.AutoScrollPosition.Y); } } ///  /// Handles the MouseFilterMove event of the mudmFilter control. ///  /// The source of the event. ///  /// The  instance containing the event data. ///  private void mouseFilter_MouseFilterMove(object sender, MouseFilterEventArgs e) { if (e.Button == MouseButtons.Left) { if (!_mouseStartPoint.Equals(Point.Empty)) { int dx = (eX - _mouseStartPoint.X); int dy = (eY - _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; } } } } ///  /// Handles the MouseFilterUp event of the mudmFilter control. ///  /// The source of the event. ///  /// The  instance containing the event data. ///  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; } } } 

我使用OP发布的代码,但发现如果面板中有像标签这样的东西,它就无法工作。 为了使这项工作更顺利,我改变了。

 e.Location 

 PointToClient(Cursor.Position) 

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

到原帖,考虑便宜的葬礼答案。

这个版本更简单,触摸更流畅(考虑手指移动速度,但不考虑移除手指后仍然滚动的效果,如移动但速度下降(也许我会这样做,我有时间))

适用于面板或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(或面板)的滚动,请创建一个inheritance自flowlayoutpanel(或面板)的usercontrol,并且:

 Imports System.Runtime.InteropServices Public Class FlowLayoutPanelExt Inherits FlowLayoutPanel  _ Private Shared Function ShowScrollBar(hWnd As IntPtr, wBar As Integer, bShow As Boolean) As  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 

这个解决方案似乎是最好的解决方案之一,也是最普遍接受的解决方案 – 但是,如果你滚动到底部并触摸按钮后面的实际流量控制(我试图这样做,以便有空的空间),你然后必须双击并按住按钮才能恢复滚动。 重新启动应用程序可恢复类似手机的滚动function。 我想知道是否有其他人已经看到这个或想出来 – 尝试使用您的应用程序,看看是否也是如此。 我修改了上面的代码段,以便您可以启动一个新项目,将其复制并粘贴到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