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

これにより、ブラウザの動作と同様に、Ctrlキーが押された場合にMouseWheelを使用してフォームのサイズが変更されます。
これは、ユーザーが見栄えが悪く、モニターの解像度が高い場合に適しています。

しかし、問題はNumericUpDownコントロールにあります。フォームからマウスホイールイベントをキャッチし、テキストボックスのようにサイズ変更は行われません。「increment」プロパティを0に設定しましたが、それは役に立ちません。

フォーム上のすべてのNumericUpDownコントロールに対してmousewheelハンドラーで特別なコードを実行する必要がないように、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」プロパティを削除する必要があることに注意してください。その後、フォントはフォームから継承されます。

4

3 に答える 3

3

MouseWheel イベントを取得するのは NUD だけではありません。Windows メッセージが処理される方法は、多くのユーザーにとって奇妙で直感的ではありません。

  • Windows は、マウス カーソルの位置に関係なく、フォーカスのあるコントロールに WM_MOUSEWHEEL メッセージを送信します。これは、多くのユーザーやプログラマーを困惑させます。彼らは、ブラウザーや Word のようなコントロールを使用しないプログラムでホイールが動作する方法に慣れています。マウスの位置に関係なく、ホイールを回すと常にページがスクロールされるプログラム。

  • 次に発生する異常なことは、メッセージがバブルすることです。フォーカスを持つコントロールがメッセージを処理しない場合、そのメッセージは親に渡されます。親がそれを処理しない場合、親の親に渡されます。など。これが、フォームにフォーカスがないにもかかわらず、フォームの MouseWheel イベントが発生する理由です。メッセージのバブリングは、Windows と Winforms ではかなり珍しいことです。しかし、ブラウザや WPF のような GUI クラス ライブラリでは珍しいことではありません。

これは、NUD がメッセージを食べる理由を説明しています。NumericUpDown、Panel、UserControl、SplitContainer、PropertyGrid、ToolStrip など、ScrollableControl から派生したコントロールをフォームに配置すると、プログラムは誤動作します。または、TreeView、ListView、TrackBar、RichTextBox、複数行の TextBox のように、当然スクロールに使用されるコントロールの場合。

これは Winforms ではかなり修正可能です。他のすべてのコントロールの予想される動作を壊すリスクがありますが、フォーカスのあるコントロールに到達する前に WM_MOUSEWHEEL メッセージを傍受し、代わりにフォームに渡すことができます。これを行うには、フォームに IMessageFilter を実装します。サンプル コードは、こちらの回答にあります


更新: Windows 10 の新しい動作に注意してください。現在、[非アクティブなウィンドウにカーソルを合わせるとスクロールする] オプションがあり、デフォルトでオンになっています。これにより、マウス ホイール メッセージが、フォーカスのあるコントロールではなく、ホバーされているコントロールに送信されます。これをオフにするユーザーはほとんどいません。これにより、マウス ホイールの使用がはるかに直感的になります。

于 2013-03-15T09:38:46.127 に答える
2

あなたの問題は、この男が抱えている問題に似ています。つまり、現在フォーカスされているためにイベント通知を盗んでいる子コントロール (NumericUpDown コントロール) がフォームにあるということです。したがって、子コントロールがそれらを処理し、親に転送しないため、親 (フォーム) はこれらのイベント通知を受け取りません。それが完全に理解できなかった場合は、ここで何が起こっているのかを正確に理解できるようになるまで、彼の質問に対する私の回答を読むことを強くお勧めします. すべての Windows プログラミングはイベント ドリブンであること、イベント通知はフォーカスされたウィンドウ/コントロールに配信されること、一度に 1 つのウィンドウ/コントロールしかフォーカスできないことを理解することが重要です。

あなたの問題の解決策も、私が他の人に提案したものと同じです。MouseWheel イベント通知の処理をより高いレベルに移動して、現在フォーカスされているコントロールによって「盗まれ」、処理されないようにする必要があります。

ただし、生の C++ の代わりに WinForms を使用しているため、彼よりもかなり簡単です。WinForms は、厄介な Win32 のすべてをオブジェクト指向 API にうまくまとめています。あなたが探しているのはメッセージフィルター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())

(はい、NumericUpDown コントロールが MouseWheel イベントを処理する方法を変更することを含む他のハッキーな解決策もあります。しかし、上で暗示したように、そのようなアプローチはNumericUpDown コントロールにのみ影響し、このイベントを処理する可能性のある他のコントロールには影響しません。親フォームにバブリングしないようにします。メッセージ フィルターをインストールするのが最もクリーンな解決策だと思います。)

于 2013-03-15T09:25:38.230 に答える
1

これに対して私が見つけた最良の解決策は、codeproject.com の小さなプロジェクトです。やり方の説明もあります。 拡張 NumbericUpDown コントロール

初期段階で mouseevent をキャンセルできないため、グローバルな解決策は考えられません (イベントをキャンセルしたため、ボタンをクリックすることはできません)。また、マウス イベントの mouseWheel 部分のみを無効にすることもほぼ不可能のようです。

于 2013-03-15T09:09:33.147 に答える