【入門編】Windows FormsのContextMenuStripにおけるOwnerDrawの活用:アイコン付きメニューやカスタムハイライトを持つリッチな右クリックUIの自作 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク
スポンサーリンク

右クリックを、芸術に変える。WinForms「OwnerDraw」で手に入れる、極上のユーザー体験

「マクロの記録機能で、とりあえず動くものを作った。でも、なんだか見た目が『いかにも業務ソフト』で、使いにくい気がする……」

もしあなたが今、そんな風に感じているなら、おめでとうございます。あなたは「単に動くコード」を書くフェーズを終え、「ユーザーに愛されるソフトウェア」を設計するエンジニアへの入り口に立っています。

今日のテーマは、Windows FormsにおけるContextMenuStrip(コンテキストメニュー)のOwnerDraw(オーナー描画)です。

標準のメニューは、OSが決めた「グレーの背景に黒い文字」というルールに従います。しかし、業務の現場では「この項目は重要だから赤くしたい」「アイコンを添えて直感的にしたい」という要望が必ず出てきます。

標準の枠組みを飛び出し、自らの手で描画(Paint)をコントロールする。この「OwnerDraw」という技術を習得すれば、あなたのアプリケーションの操作性は劇的に進化します。さあ、一緒にその扉を開けてみましょう。

1. 「OwnerDraw」とは何か? —— 描画の主権を取り戻す

通常、Windows Formsのコントロールは、Windows OSが「こう描いてね」と指示した通りに表示されます。これを「システム描画」と呼びます。

しかし、`OwnerDraw` プロパティを `True` に設定すると、OSに対してこう宣言することになります。
「描画の指示は、OSではなく、私が直接行います」

これが「OwnerDraw(所有者による描画)」の正体です。このモードに切り替えると、メニューの項目(MenuItem)は真っ白なキャンバスになります。そこに、色を塗り、アイコンを配置し、フォントを調整するのは、すべてあなたの書くコードの仕事になります。

2. 実装のグランドデザイン:3つのステップ

OwnerDrawを実装するには、以下の3つのステップを理解する必要があります。

1. スイッチを入れる: `ContextMenuStrip` の各項目の `OwnerDraw` プロパティを `True` にする。
2. キャンバスに描く: `DrawItem` イベントの中で、`e.Graphics`(筆)を使って描画する。
3. 状態を判別する: 「マウスが乗っているか(ハイライト)」「選択されているか」を判定して、見た目を変える。

ここからは、実際に動くコードを見ながら、その本質を学んでいきましょう。

3. 実践:リッチなカスタムメニューの実装

以下のコードは、アイコン付きで、かつマウスホバー時に美しいハイライトを表示するカスタムメニューの完全なサンプルです。

Imports System.Drawing
Imports System.Windows.Forms

Public Class RichMenuForm
    Inherits Form

    Private WithEvents myContextMenu As New ContextMenuStrip()

    Public Sub New()
        ' フォームの基本設定
        Me.Text = "OwnerDraw Masterclass"
        Me.Size = New Size(400, 300)
        Me.StartPosition = FormStartPosition.CenterScreen

        ' --- 1. メニュー項目の作成 ---
        ' 通常の作成方法と同じですが、OwnerDrawをTrueにするのが肝です
        Dim item1 As New ToolStripMenuItem("重要:データ更新", Nothing, HandleUpdateClick)
        item1.OwnerDraw = True ' これが魔法のスイッチ!
        item1.Tag = "Critical" ' 描画の判定に使うための目印(タグ)

        Dim item2 As New ToolStripMenuItem("標準:設定変更", Nothing, HandleSettingsClick)
        item2.OwnerDraw = True
        item2.Tag = "Normal"

        Dim item3 As New ToolStripMenuItem("終了", Nothing, AddressOf Me.Close)
        item3.OwnerDraw = True
        item3.Tag = "Exit"

        ' メニューに項目を追加
        myContextMenu.Items.Add(item1)
        myContextMenu.Items.Add(item2)
        myContextMenu.Items.Add(item3)

        ' フォームに右クリックメニューを紐付け
        Me.ContextMenuStrip = myContextMenu

        ' --- 2. 描画イベントの紐付け ---
        ' OwnerDrawをTrueにすると、このDrawItemイベントが呼ばれるようになります
        AddHandler myContextMenu.DrawItem, AddressOf MyContextMenu_DrawItem
    End Sub

    ''' <summary>
    ''' メニューの各項目を描画する心臓部
    ''' </summary>
    ''' <param name="e">描画情報が詰まったオブジェクト</param>
    Private Sub MyContextMenu_DrawItem(sender As Object, e As DrawItemEventArgs)
        ' e.Graphics が、私たちが使う「筆」です
        Dim g As Graphics = e.Graphics
        Dim item As ToolStripMenuItem = TryCast(e.Item, ToolStripMenuItem)

        If item Is Nothing Then Return

        ' 描画範囲(矩形)を取得
        Dim rect As Rectangle = e.Bounds

        ' --- A. 背景の描画(ハイライト処理) ---
        ' マウスが乗っているか、または選択されている場合の色を変える
        If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
            ' ハイライト時の色(少し洗練された濃いブルー)
            Using highlightBrush As New SolidBrush(Color.FromArgb(0, 120, 215))
                g.FillRectangle(highlightBrush, rect)
            End Using
            Dim textColor As Color = Color.White
            Dim iconColor As Color = Color.White
        Else
            ' 通常時の背景(透明、またはシステムのデフォルト)
            Dim textColor As Color = Color.Black
            Dim iconColor As Color = Color.DimGray
        End If

        ' --- B. アイコンの描画(カスタムロジック) ---
        ' Tagプロパティを見て、描画する色や形を変えるテクニック
        Dim iconRect As Rectangle = New Rectangle(rect.X + 5, rect.Y + 4, 16, 16)
        
        ' Tagの内容によって、描画する「記号」を変えてみましょう
        Select Case item.Tag.ToString()
            Case "Critical"
                ' 重要項目は赤い丸を描画
                Using brush As New SolidBrush(Color.Red)
                    g.FillEllipse(brush, iconRect)
                End Using
            Case "Normal"
                ' 通常項目は青い四角を描画
                Using brush As New SolidBrush(Color.RoyalBlue)
                    g.FillRectangle(brush, iconRect)
                End Using
            Case "Exit"
                ' 終了はグレーの×を描画
                Using pen As New Pen(Color.Gray, 2)
                    g.DrawLine(pen, iconRect.X, iconRect.Y, iconRect.Right, iconRect.Bottom)
                    g.DrawLine(pen, iconRect.Right, iconRect.Y, iconRect.X, iconRect.Bottom)
                End Using
        End Select

        ' --- C. テキストの描画 ---
        ' アイコンの分だけ右にずらして描画
        Dim textRect As Rectangle = New Rectangle(rect.X + 30, rect.Y, rect.Width - 35, rect.Height)
        
        ' TextRendererを使うのが、Windowsの標準フォントに最も綺麗に合わせるコツです
        ' Graphics.DrawString よりも、文字のアンチエイリアスが非常に綺麗になります
        TextRenderer.DrawText(g, item.Text, Me.Font, textRect, textColor, TextFormatFlags.VerticalCenter)
    End Sub

    ' --- イベントハンドラ(クリック時の動作) ---
    Private Sub HandleUpdateClick(sender As Object, e As EventArgs)
        MessageBox.Show("データを更新しました!")
    End Sub

    Private Sub HandleSettingsClick(sender As Object, e As EventArgs)
        MessageBox.Show("設定画面を開きます。")
    End Sub
End Class

4. 伝説のエンジニアが教える「陥りやすい罠」と解決策

ここをクリアすれば、あなたはもう初心者ではありません。実装時に必ず直面する、プロの現場での「落とし穴」を伝授します。

① `TextRenderer` を使いなさい

多くの初学者は `g.DrawString` を使って文字を描画しようとします。しかし、WinFormsの標準的なテキストは `TextRenderer.DrawText` を使って描画されています。`DrawString` を使うと、文字が微妙にボヤけたり、位置がズレたりして「安っぽいUI」になってしまいます。「文字は TextRenderer」、これは鉄則です。

② リソースの解放(Dispose)を忘れるな

コードの中で `New SolidBrush(…)` や `New Pen(…)` を行っていますね。これらは「GDI+リソース」と呼ばれる、OSの貴重なメモリ資源を消費します。
必ず `Using` ブロックを使って、使い終わったら即座に破棄するようにしてください。これを怠ると、アプリケーションを長時間起動した際に「メモリリーク」を起こし、最終的にアプリがクラッシュします。

③ `DrawItem` は「描画」だけに専念させる

`DrawItem` イベントは、マウスを動かすたびに、あるいは画面が更新されるたびに、猛烈な勢いで呼び出されます。
ここで「データベースにアクセスする」「重い計算をする」といった処理を書いてはいけません。描画は、「今、あるべき姿を素早く描く」ことだけに集中させてください。重い処理は、クリックイベント(`Click`)の方で行うのが設計の王道です。

5. 最後に:一歩先へ進むあなたへ

OwnerDrawをマスターするということは、Windowsという巨大なシステムに対して、「自分のルール」を適用できるようになるということです。

最初は、`e.Graphics` の使い道に戸惑うかもしれません。座標がズレたり、色が思い通りに出なかったりすることもあるでしょう。でも、大丈夫です。一つひとつのメソッドを試し、キャンバスを汚しながら学んでいくプロセスこそが、エンジニアとしての血肉になります。

「標準機能でできること」の限界を感じたとき、この技術を思い出してください。
あなたの書くコードが、ユーザーにとって「使いやすい」だけでなく「心地よい」ものになることを、私は心から応援しています。

さあ、次はどんな美しいUIを描きますか?

タイトルとURLをコピーしました