2

我只是找到了一种根据表单的字体大小调整表单(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 的简短教程,我为 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”属性。然后字体将从表单继承。

4

3 回答 3

3

你有一个更大的问题,不仅仅是 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 来做到这一点。您可以在我的答案中找到示例代码。


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

于 2013-03-15T09:38:46.127 回答
2

您的问题与这个人遇到的问题类似,即您的表单上有一个子控件(N​​umericUpDown 控件)正在窃取事件通知,因为它当前是焦点。因此,父级(您的表单)没有收到这些事件通知,因为子控件正在处理它们而不是将它们转发给其父级。如果这对您来说不完全有意义,我强烈建议您阅读我对他的问题的回答,直到您觉得您了解这里到底发生了什么。重要的是要了解所有 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())

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

于 2013-03-15T09:25:38.230 回答
1

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

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

于 2013-03-15T09:09:33.147 回答