メニューを動的に追加する(前編)

 メニューを追加したり削除したりというのは、実際にはあまりユーザーフレンドリーではありません。通常は、メニューの左にチェックボタンを付けたり、淡色表示させて無効化させたりのいわゆる「トグルスイッチ」にした方が使いやすいでしょう。

 ですが、メニューを追加・削除した方がいい場合も数多く存在します。例えば、ユーザーが使いやすいようにメニューを自由にエディットする場合や、自由でなくても特定のメニューにユーザーの定義を設ける場合があります。また、「最近使ったファイル」や、ネットスケープのブックマーク表示など、とにかくどんどん増えていくものを追加する場合もあります。

 ではまず、簡単にメニューを追加する方法を見ていきましょう。


 まず簡単に、どういうアプリケーションを作るか説明すると、「メニューの追加」というメニューを選択すると、その下に「追加」というメニューが増えていき、その「追加」を選択するとメッセージボックスが表示されるようなアプリケーションをテストとして作ってみます。

 まず、SDIのアプリケーションを作ります。次に、メニューのIDR_MAINFRAMEに、次のような形でメニューを追加します。

メニュー
 いちばん根本のメニューに適当なメニューをポップアップを作り、その下にID_ADDMENUのメニューを作ります。そして、その下に線を引いておきます。
 アプリケーションの目標は、この「メニューの追加」を選択するごとに、この線の下にメニューを追加していくというものです。

 まず、このコマンドに関連づけられたメンバ関数を作成します。別にどのクラスでも構わないのですが、今回はビュークラスに作成します。ビュークラスの.cppファイルを開き、オブジェクトのIDをID_ADDMENUに、メッセージをCOMMANDにして関数を作成します。

 そして、次のようにコーディングします。


void CKABView::OnAddmenu() 
{
	///////////////
	// メニューを加えます。
	// トップメニューを取得します。
	CMenu* pcTopMenu = AfxGetMainWnd()->GetMenu();
	int iPos;
	// メニューを検索します。
	for( iPos = pcTopMenu->GetMenuItemCount() - 1; iPos >= 0; iPos-- )
	{
		// サブメニューをチェックします。
		CMenu* pcThisMenu = pcTopMenu->GetSubMenu( iPos );
		// もしサブメニューの最初のメニューがID_ADDMENUなら……
		if( pcThisMenu && pcThisMenu->GetMenuItemID( 0 ) == ID_ADDMENU )
		{
			//メニューを追加します。
			pcThisMenu->AppendMenu( MF_STRING, ID_NEWMENU, _T( "追加" ) );
		}
	}	
}
	

 順を追って見ていきましょう。
 まずCMenu型のポインタpcTopMenuを宣言し、そこに、今使っているメニュー(つまりIDR_MAINFRAME)のポインタを代入します。
 気を付けて欲しいのは、このメニューはIDR_MAINFRAME全体ではなく、最初の横一列だけだということです。CMenu型は実は、ツリー形式のようなメニュー全体を格納するのではなく、あくまで一列ずつしか格納しません。
 そこで、まず最初の横一列だけを取得したというわけです。

 さて、取得したら、その中からまた下へと伸びるメニューの一列を取得しなければなりません。そこで、横一列のメニューの中をひとつひとつ検索していきます。
 まず横一列のメニューの数をGetMenuItemCount()メンバ関数で得ます。横一列のメニューの各ポップアップメニューには、左から0、1、2……と数字が充てられていて、GetSubMenu( iPos )メンバ関数iPosにその数字を入れるとそこから下へと伸びるメニューへのポインタを返してくれます。
 そこで、もうひとつCMenu型のポインタpcThisMenuを作成し、その中に今得た下へと伸びるメニューを格納します。

 そうしたら、下へと伸びるメニューの中身をチェックします。GetMenuItemID( 0 )メンバ関数を使うと、指定の位置のメニューのIDが判ります。そこで、メニューに作っておいたID_ADDMENUがいちばん上にあるかどうかチェックすればいいわけです。

 チェックして、その下に伸びるメニューのいちばん上にID_ADDMENUがなければ、次の下に伸びるメニューをチェックしていきます。forループはそのためにあります。

 めでたく見つかれば、AppendMenuメンバ関数を使ってメニューを追加します。

(ちなみに、今回の場合はこんなめんどくさい方法する必要はありません。最初っからメニューの位置が判ってるんだから、メニューの検索なんてする必要はありません。
 簡単にしたいときには次のようにしてください。


void CKABView::OnAddmenu() 
{
	///////////////
	// メニューを加えます。
	// トップメニューを取得します。
	CMenu* pcTopMenu = AfxGetMainWnd()->GetMenu();
	// サブメニューを取得します。
	CMenu* pcThisMenu = pcTopMenu->GetSubMenu( 2 );
	// メニューを追加します。
	pcThisMenu->AppendMenu( MF_STRING, ID_NEWMENU, _T( "追加" ) );
}
	

 簡単でしょ? 今回は、最終的にはどんどんメニューを追加しようと思うので、こういう説明にしました。)

 さて、この状態でビルドすれば、メニューの追加はできているでしょう。しかし、このままでは新しく追加された「追加」メニューは何の用も成しません。そこで、このメニューに結びつけられたメンバ関数を作成します。

 一番簡単な方法は、新しくメニューリソースを作成してしまい、その中に「追加」メニューを作ってしまうことです。別に名前は「追加」でなくてもいいです。とにかくIDがID_NEWMENUのメニューを作成してしまいます。
 そのあと、オブジェクトのIDにID_NEWMENUを、メッセージにCOMMANDを選択してメンバ関数を新たに作成し、その中に次のようなコーディングを行えばいいのです。


void CKABView::OnNewmenu() 
{
	///////////////
	// メッセージボックスを表示します。
	MessageBox( _T( "追加されたぞ" ), _T( "追加されたらしい" ) );
}
	

 これで、「メニューを動的に追加し、そのメニューに機能を付ける」ということは達成しました。

 ……ん、でも待てよ? というわけで、後編へと続く――

(C)KAB-studio 1997 ALL RIGHTS RESERVED.