【问题标题】:MouseWheel event on NumericUpDownNumericUpDown 上的 MouseWheel 事件
【发布时间】:2013-03-15 08:36:48
【问题描述】:

我只是找到了一种根据表单的字体大小调整表单(winforms)大小的方法。
为此,我在表单的 MouseWheel 事件处理程序中制定了例程:

Private Sub myfrm_MouseWheel(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseWheel

     If controlpressed Then
        fs = Me.Font.Size
        If e.Delta < 0 Then
            If fs < 16 Then fs += 1
        Else
            If fs > 8 Then fs -= 1
        End If
        Me.Font = New Font("Arial", fs, FontStyle.Regular, GraphicsUnit.Point)
    End If

如果按下控制键,则使用 MouseWheel 调整我的表单大小,类似于浏览器的行为。
这在用户看不到好并且显示器分辨率高的情况下非常有用。

但问题在于 NumericUpDown 控件。他们从表单中捕获鼠标滚轮事件,并且不会像文本框那样调整大小。我将“增量”属性设置为 0,但这没有帮助。

这里有什么方法可以告诉 NumericUpDown 控件它们不会捕获鼠标滚轮事件,因为我不需要在鼠标滚轮处理程序中为表单上的每个 NumericUpDown 控件执行一些特殊代码?

编辑:根据 Cody Gray 的简短教程,我给出了为 winform 应用“缩放”功能的完整代码。

在启动表单的 _Load 事件处理程序:

Application.AddMessageFilter(New MouseWheelMessageFilter())

类:

Public Class MouseWheelMessageFilter
Implements IMessageFilter
Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
    Const WM_MOUSEWHEEL As Integer = &H20A
    If m.Msg = WM_MOUSEWHEEL And My.Computer.Keyboard.CtrlKeyDown Then
        If Form.ActiveForm IsNot Nothing Then
            Try 'this solves too fast wheeling
                Dim delta As Integer = m.WParam.ToInt32() >> 16
                Dim fs As Single = Form.ActiveForm.Font.Size
                If delta < 0 Then
                    If fs < 16 Then fs += CSng(0.25)
                Else
                    If fs > 8 Then fs -= CSng(0.25)
                End If
                Form.ActiveForm.Font = New Font("Microsoft Sans Serif", fs!, FontStyle.Regular, GraphicsUnit.Point)
            Catch
            End Try
        End If
        Return True
    End If
    Return False
End Function
End Class

这适用于项目中的所有表单。 出乎意料的好用!

注意,为了在调整文本框大小时实现完全缩放效果,因此需要打开 form.Designer 文件并删除每个控件的“.font”属性。那么字体会继承自form。

【问题讨论】:

    标签: vb.net


    【解决方案1】:

    你有一个更大的问题,不仅仅是 NUD 抓住了 MouseWheel 事件。对于许多用户来说,处理 Windows 消息的方式是不寻常且不直观的:

    • 无论鼠标光标位于何处,Windows 都会将 WM_MOUSEWHEEL 消息发送到具有焦点的控件。这难倒了许多用户和程序员,他们已经习惯了滚动在浏览器或 Word 等程序上的工作方式,这些程序不使用任何控件。与鼠标位置无关的程序,滚轮总是滚动页面。

    • 接下来发生的不寻常的事情是消息冒泡,如果具有焦点的控件不处理该消息,则将其传递给其父级。如果父母不处理它,那么它将被传递给父母的父母。等等。这就是表单的 MouseWheel 事件触发的原因,即使表单从未获得焦点。消息冒泡在 Windows 和 Winforms 中非常不寻常。但在浏览器或 WPF 等 GUI 类库中并不少见。

    所以这就解释了为什么 NUD 会吃掉这个消息,它有一个用途。您还没有遇到其他的,当您将任何从 ScrollableControl 派生的控件放在窗体上时,您的程序也会出现异常,例如 NumericUpDown、Panel、UserControl、SplitContainer、PropertyGrid 或 ToolStrip。或者当一个控件自然具有滚动用途时,例如 TreeView、ListView、TrackBar、RichTextBox、多行 TextBox。

    这在 Winforms 中是完全可以修复的,冒着破坏所有其他控件的预期行为的风险,您可以在 WM_MOUSEWHEEL 消息到达具有焦点的控件之前拦截它并将其传递给您的表单。您可以通过在表单中​​实现 IMessageFilter 来做到这一点。您可以在my answer here 中找到示例代码。


    更新:请注意 Windows 10 中的新行为,它现在具有“当我将鼠标悬停在非活动窗口上时滚动”选项,并且默认情况下处于打开状态。这会导致将鼠标滚轮消息发送到悬停的控件而不是具有焦点的控件。很少有用户会关闭它,它使使用鼠标滚轮更加直观。

    【讨论】:

      【解决方案2】:

      您的问题与this guy 的问题类似,即您的表单上有一个子控件(N​​umericUpDown 控件)正在窃取事件通知,因为它当前是焦点。因此,父级(您的表单)没有收到这些事件通知,因为子控件正在处理它们而不是将它们转发给其父级。如果这对您没有完全意义,我强烈建议您阅读my answer to his question,直到您觉得您了解这里到底发生了什么。重要的是要了解所有 Windows 编程都是事件驱动的,事件通知被传递到焦点窗口/控件,并且一次只能有一个窗口/控件获得焦点。

      您的问题的解决方案也与我向其他人提出的解决方案相同。您需要将 MouseWheel 事件通知的处理提升到更高的级别,以确保它不会被“窃取”并由当前聚焦的控件处理。

      不过,您的事情比他容易得多,因为您使用的是 WinForms 而不是原始 C++。 WinForms 将所有凌乱的 Win32 东西都很好地包装在一个面向对象的 API 中。您要查找的内容称为消息过滤器,它在WinForms 中以IMessageFilter 接口的形式实现。您可以通过调用Application.AddMessageFilter 函数并指定您的消息过滤类(实现IMessageFilter 接口)来添加消息过滤器。在您的消息过滤类中,您将实现IMessageFilter.PreFilterMessage 函数,该函数决定是否应将消息传递给它所指定的特定控件。您要做的是检查消息是否为WM_MOUSEWHEEL,如果是,请自行处理而不是让它传递。这将阻止所有子控件(不仅仅是 NumericUpDown 控件)处理 MouseWheel 事件,并确保该事件在整个应用程序中的行为一致。

      一点点示例代码:

      Public Class MouseWheelMessageFilter : Implements IMessageFilter
          Public Function PreFilterMessage(ByRef m As Message) As Boolean
            ' Filter out WM_MOUSEWHEEL messages, which raise the MouseWheel event,
            ' whenever the Ctrl key is pressed. Otherwise, let them through.
            Const WM_MOUSEWHEEL As Integer = &H20A
            If m.Msg = WM_MOUSEWHEEL && My.Computer.Keyboard.CtrlKeyDown Then
               ' Process the message here.
               If Form.ActiveForm IsNot Nothing Then
                  ' TODO: Insert your code here to adjust the size of the active form.
                  ' As shown above in the If statement, you can retrieve the form that
                  ' is currently active using the static Form.ActiveForm property.
                  ' ...
               End If
               Return True  ' swallow this particular message
            End If
            Return False    ' but let all other messages through
         End Function
      End Class
      

      在显示第一个表单之前,您还需要确保将对 Application.AddMessageFilter 的调用添加到应用的 Main 函数中:

      Application.AddMessageFilter(New MouseWheelMessageFilter())
      

      (是的,还有其他一些棘手的解决方案涉及修改 NumericUpDown 控件处理 MouseWheel 事件的方式。但是,正如我在上面暗示的那样,这些方法只会影响 NumericUpDown控件,而不是其他可能也处理此事件的控件,从而防止它冒泡到您的父窗体。我认为安装消息过滤器是迄今为止最干净的解决方案。)

      【讨论】:

      • 射击,你比我还快。至少我比你高了五年;)
      • 嗨 Cody Gray,感谢您对问题的广泛解释。但是我想知道这个世界上是否有人知道为什么文本框可以将鼠标滚轮传递给表单而 NumericUpDown 不能?我相信,MS 中的那些人也不知道这个问题的答案!到目前为止,我在 winforms 编程中发现了很多点,这些 GUI 系统可以因发布的最不一致的 GUI 工具而获奖。
      • @user973238 文本框不处理 MouseWheel 事件,因为它们没有用处——它们不滚动。因为他们不处理它,所以它会冒泡到下一个可用的处理程序,即它的父控件(您的表单)。 NumericUpDown 控件确实 处理 MouseWheel 事件,因为它可以用它做一些有用的事情——调整它显示的值。而且因为它处理它,它不会将它传递给它的父级(你的表单)。 WinForms 的问题在于它反映了大约 20 年前首次编写 Windows 时所做的决定,它只是将其包装在一个漂亮的包中。
      • 是的,我明白了。但我们在关键事件上也有类似的情况。如果我们将 Form 的 keypreview 设置为 true 表单将在控制之前捕获事件!所以我认为,如果我将“增量”设置为 0 NumericUpDown 可以将这些事件传递给更低。但好吧,只是我的想法。现在我将尝试实施您的建议和示例。
      • @user973238 KeyPreview 是完全不同的东西,添加只是为了向后兼容 VB 的 pre-.NET 版本。它实现了与我在这里描述的类似的东西,但用于键盘事件,而不是鼠标事件。将 NumericUpDown 控件的 Increment 属性设置为 0 不起作用的原因是因为这不会禁用拦截滚轮通知的控件。这只是意味着它不会任何响应通知,因为您告诉它该值不能增加。对不起,这令人困惑!
      【解决方案3】:

      我为此找到的最佳解决方案是 codeproject.com 中的一个小项目。还有一个解释如何做到这一点。 Extended NumbericUpDown Control

      我想不出一个全局解决方案,因为您无法在早期阶段取消鼠标事件(您将无法单击按钮,因为您取消了事件)。而且只禁用鼠标事件的mouseWheel部分似乎也几乎是不可能的。

      【讨论】:

      • 您确实可以在早期阶段全局取消鼠标滚轮。事实上,这正是你想要的。请记住,您只处理 MouseWheel 事件,这是滚动滚轮时引发的事件。它不会阻止所有点击工作,它只是对滚轮的行为进行全局更改,这正是他想要的。详情见我的回答。
      • 嗨 Marius,不喜欢这个解决方案,因为我的 NumericUpDown 控件已经“扩展”了一些重要的行为。
      猜你喜欢
      • 2011-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-06
      • 1970-01-01
      • 2019-05-14
      • 1970-01-01
      相关资源
      最近更新 更多