NumericUpDown上的MouseWheel事件

时间:2013-03-15 08:36:48

标签: vb.net

我只是找到一种基于表单字体大小来调整表单(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控件。他们从表单中捕获mousewheel事件,并且不会像文本框一样调整大小。我将“增量”属性设置为0,但这没有帮助。

有没有办法告诉NumericUpDown控件他们没有抓住鼠标滚轮事件,我不需要在窗体上的每个NumericUpDown控件的mousewheel处理程序中做一些特殊的代码?

编辑:基于Cody Gray的简要教程,我给出了为winforms应用“缩放”功能的完整代码。

在启动表单的_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”属性。然后字体将从表单继承。

3 个答案:

答案 0 :(得分:3)

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

  • Windows将WM_MOUSEWHEEL消息发送到具有焦点的控件,无论鼠标光标位于何处。这使得许多用户和程序员都感到困惑,他们习惯于在浏览器或Word等程序上运行,这些程序不使用任何控件。鼠标所在位置无关紧要的程序,滚动总是滚动页面。

  • 发生的下一个不寻常的事情是消息 bubbles ,如果具有焦点的控件不处理消息,则将其传递给其父节点。如果父级没有处理它,那么它将被传递给父级的父级。等等。这就是为什么你的表单的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中的新行为,它现在具有“当我将鼠标悬停在它们上时滚动非活动窗口”选项,并且默认情况下它已打开。这导致鼠标滚轮消息被发送到正在悬停的控件而不是具有焦点的控件。很少有用户会关闭它,这使得使用鼠标滚轮更加直观。

答案 1 :(得分:2)

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

你的问题的解决方案也与我向另一个人提出的解决方案相同。您需要将MouseWheel事件通知的处理移至更高级别,以确保它不会被当前关注的控件“窃取”并处理。

但是,你有比他更容易的东西,因为你使用的是WinForms而不是原始的C ++。 WinForms在面向对象的API中很好地包装了所有混乱的Win32内容。您正在寻找的内容称为消息过滤器,并在IMessageFilter接口方面在WinForms中实现。您可以通过调用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())

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

答案 2 :(得分:1)

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

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