【WPF】簡単サンプル満載!ContextMenu攻略

当ページのリンクには広告が含まれています。

自作アプリケーションに色々な機能を付ける場合、アイコンボタンを用意することが多いですが、アイコンボタンは画面に場所を取るのでそんなに多くは配置できません。

その点、コンテキストメニューは右クリックした時のみ表示され、また階層化もできるので、場所も取らず多くの機能を盛り込むことが出来ます。

今回は、WPFで右クリックによるコンテキストメニューの設定方法と、メニューが選択された際のイベントの扱い方について解説したいと思います。

目次

コンテキストメニューとは

コンテキストメニューとは、ウィンドウやコントロールにマウスを当てて右クリックしたときに表示されるメニューのことです。

下記のサンプルはWPFのDataGridに対してコンテキストメニューを付けた場合の動作例です。

コンテキストメニューの使い方

まず、コンテキストメニューの表示方法について解説したいと思います。

最初に概要を図を使って説明し、次に具体的なソースコードを紹介します。

コンテキストメニューの表示方法

まず、コンテキストメニューは何らかの親(コントロール又はWindow)に所属させる必要があります。

その為、少し回りくどい記述になりますが、下記の様に親コントロールのプロパティに対して、ContextMenuクラスをセットし、その中に個々のメニューとなるMenuItemを入れていくことになります。

個々のメニューに表示させたいメニュー名は、MenuItemのHeaderプロパティにセットして下さい。

ラベルやボタンにコンテキストメニューを付けるケースは少ないかもしれませんが、仮にラベルにコンテキストメニューを付けたい場合は次の様になります。

メニューの階層化(入れ子)

メニューを階層化したい場合は、MenuItemの中にMenuItemを入れ子にします。

入れ子は何階層でも可能ですが、あまり階層を深くすると選択するのが面倒になるので、2~3個程度に留めておくのが無難です。

先ほど例に出したラベルに対して2階層のメニューを付けるには、次の様になります。

サンプル画面のXAML

以上の事を念頭に、先ほどのサンプル画面のコンテキストメニューを実現するXAMLは次の通りです。

選択されたメニューの取得方法

メニューが選択されたことを知るには、大きく分けて2通りの方法があります。

1つは RoutedCommand 等を使ってC#のソースコード上にコマンド受け付けようのメソッドを用意しておき、選択されたメニューに対応するCommand をXAMLに記述する方法、もう1つはクリックイベントを使う方法です。

Command を使う方法はXAMLとC#のソースの結びつきが弱くなるため、メニューの機能をボタンに置き換えたりする場合などが柔軟に対応できますが、記述量が多くなって面倒なので、ここではクリックイベントを使う方法を採用します。

メニューごとにクリックイベントを用意する

次の様に1つ1つのMenuItemにイベントハンドラを用意し、クリック時にそれを呼ぶようにすることで、メニューが選択されたことを簡単に知ることができます。

ただ、全てのItemMenuに名前を付与する必要があることと、メニューの数が多くなるほどイベントハンドラの数が増え、ソースコードが複雑になるという課題はあるものの、一番単純な方法です。

1つのイベントハンドラでまとめて受ける

メニューの数が多くイベントハンドラを沢山作りたくない場合、全てのMenuItem に同じイベントハンドラを指定し、1か所で処理をまかなうという方法があります。

この場合、個々のメニューに対して名前を付ける必要はありません。

この方法は、イベントハンドラ側で MenuItemの Header を取得し、その中身で処理を切り分けますので、個々のメニューが識別できるようなメニュー名にすることと、メニュー名を不用意に変更するとイベントハンドラ側で識別できなくなってしまうという課題はあります。

イベントハンドラ側のソースコードは次の様に記述できます。

それでは、第1階層は異なるが、第2階層は同じメニュー名にしたいとう場合はどうすればいいでしょう?

例えば、

MenuItemには Parent プロパティがあり、これを MenuItem に型変換(キャスト)することで、親(1つ上の階層)のMenuItemが取得できますので、次の様に書くことが出来ます。

親階層のMenuItemのクリックイベントを使ってメニューを判定する

メニューが階層になっている場合、親のメニューにクリックのイベントハンドラを指定しておけば、 RoutedEventArgs e の OriginalSource プロパティを使って、選択された MenuItem を取り出すことが出来ます。

この方法は、ItemMenuを入れ子にして、ドロップダウン式に選択するようなメニューの場合、その親のItemMenuにクリックイベントを指定するだけで済むため、クリックイベントの数を大幅に減らすことができます。

メニューにアイコンを入れる

ItemMenuにアイコンを表示するには、MenuItemの Icon プロパティにImageを設定する必要があります。

具体的には、アイコンを表示しない場合とする場合では、次の様に MenuItem の記述が変わります。

表示するアイコンのイメージはどこに置いても良いのですが、次の様にプロジェクト内にImage(もしくはImages)という名前のフォルダを作成し、そこに png 型式のイメージファイルを置き、それを参照するケースが多いようです。

イメージファイルの保存先や参照方法についての詳細については、こちらに詳しく記載しています。

この記事では Image フォルダ直下にイメージファイルが置かれていることを想定して解説します。

次の画像はDataGridのコンテキストメニューにアイコンを表示した例です。

では、実際のXAMLを見ていきましょう。

Imageタグの Source にイメージファイルのパスを指定する必要がありますが、今回はプロジェクトフォルダ内に作ったImageフォルダにイメージファイルがコピーされているので、相対指定で<Image Source=”Image/InputPinTool_16x.png”/> と記述しています。

イメージファイルの頭に “Image/” を記述するところがポイントです。

メニューに任意のコントロールを入れる

コンテキストメニューには任意のコントロールを入れる事も可能です。

この例では、第1階層目にテキストボックスとボタンを、第2階層目にテキストブロック、テキストボックス、ボタンを入れています。

第1階層目には通常 MenuItem を置きますが、任意のコントロールを置く事も可能です。

MenuItemの代わりに、テキストボックスやボタンを置けば、文字列の入力やボタンクリックが行えるようになります。

例えば、第1階層目にテキストボックス、ボタンを表示させたい場合、次の様になります。

第2階層目以降は、MenuItemの中に任意のコントロールを入れ子にします。

例えば、MenuItemにテキストボックスを入れたい場合は次の様になります。

サンプル画面では1つのMenuItemに複数のコントロール(ラベル、テキストボックス、ボタン)を入れてますが、通常MenuItemには1つのコントロールしか入れられません。

WPFではよく行う手法ですが、GridやStackPanel等を定義し、その上に複数のコントロールを置くという方法を使っています。

それでは、サンプル画面の実際のXAMLを掲載しておきます。

これまで説明した内容を踏まえてソースコードを見て頂ければ、内容が理解できるかと思います。

メニューを開いた時、動的にコントロールに値をセットする

コンテキストメニューに入れたコントロールに対して初期値を設定したい場合は、XAML上で簡単に設定できます。

しかし、時にはプログラムの動作に応じて、動的に初期値を変更したい場合もあります。

例えば、タブコントロールを使ったプログラムにおいて、選択されているタブ名前を変更するような場合です。

コンテキストメニューのサブメニューを開くと SubmenuOpened イベントが発生しますので、これを使ってコントロールに初期値を登録することが可能です。

下記のサンプルは、TabControlのコンテキストメニューに対して、SubmenuOpened イベントを使った例になります。

下記はC#側のソースコードです。

チェックが入力できるメニューを作る

何らかの機能をON/OFFするようなメニューを作成したい場合、チェックボックスコントロールを入れてしまうという方法もありますが、実はMenuItemの IsCheckable プロパティに True を設定してあげることで、簡単に実現できます。

下記は Button タグに IsCheckable=”True” を記述したXAMLのサンプルです。

チェックがされているか否かを取得するには、 MenuItem の IsChecked プロパティを使います。

これは通常のチェックボックスと同じプロパティなので、if 文で参照する場合は (bool)IsChecked という具合に型変換をしてから参照します。

初期値としてチェックを入れておきたい場合は、これもチェックボックスと同様に、 IsChecked プロパティに true を設定しておきます。

この方法には一つ落とし穴があって、メニューを入れ子にしていた場合、「親階層のMenuItemのクリックイベントを使ってメニューを判定する」という方法がそのまま使えません。

どういうことかというと、親階層のクリックイベントの sender で渡ってくる情報は、あくまでも親のMenuItemの情報であり、子供のMenuItemが渡ってくる訳ではないからです。

つまり、IsCheckable=”False” の場合、親のクリックイベントで次のように記述すれば、実際にクリックされたメニューのヘッダが取得できましたが、IsCheckable=”True” の場合、親のヘッダしか取得できません。

ただ、親のMenuItemにある Items コレクションには、子供のItemMenuが登録されているので、ここから子供のItemMenuを取り出し、チェックされているか否かを IsChecke プロパティで判定することは可能です。

この方法を使うと、次のように記述できます。

まとめ

今回はWPFにおけるコンテキストメニューの使い方について解説しました。

WindowsForm と比べて、WPFのコンテキストメニューはかなり自由度が高いのではないでしょうか。

XAMLの記述量は増えますが、ちょっとした入力機能なら簡単にメニューに含ませることが出来ます。

アプリケーションに機能を追加する際、ボタンの配置では足らないと思った場合は、一度コンテキストメニューの利用についてもご検討ください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次