では、ではメニューで選択された項目をどのようにして知ればよいのでしょうか。 SDKでは、WM_COMMANDメッセージの処理でした。WM_COMMANDメッセージを 捕まえて、この時のLOWORD(wParam)がシンボルでした。MFCでは メッセージマップエントリで行います。
ON_COMMAND(シンボル名、処理関数)
とします。関数名は何でもいいのですが、OnPaintとかOnChar関数のように Onで始まる名前にするのが一般的です。
メニューがついて少しWindowsのプログラムらしくなりました。
クライアント領域に表示されている文字はウィンドウの大きさを変えても
ほぼ中央に来るようにしてみました。
では、早速プログラムの中身を見てみましょう。
普通のメニューリソースです。ここで定義したシンボルはIDM_ENDとIDM_ABOUTです。// menu01.rcの一部です。 ////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "ヘルプ(&H)" BEGIN MENUITEM "About", IDM_ABOUT END END
今回もクラス定義部分をヘッダーファイルに分離しました。 メニューから「終了」とか「About」などが選択されたときに呼ばれるのが OnEnd(), OnAbout()の自作関数です。// menu01.h class CMyApp : public CWinApp { public: virtual BOOL InitInstance(); }; class CMyWindow : public CFrameWnd { public: CMyWindow(); //コンストラクタ afx_msg void OnPaint(); afx_msg void OnEnd(); afx_msg void OnAbout(); DECLARE_MESSAGE_MAP() };
ソースファイルの最初の方です。シンボル定義ファイル"resource.h"の インクルードを忘れないでください。自作の"menu01.h"も忘れないでください。// menu01.cpp #include <afxwin.h> #include "resource.h" #include "menu01.h" CMyApp MyApp; BOOL CMyApp::InitInstance() { m_pMainWnd = new CMyWindow(); m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE; }
CMyWindowクラスのメッセージマップです。 メニューからIDM_ENDが選択されたらOnEnd()関数が呼ばれ、 IDM_ABOUTが選択されたら、OnAbout()関数が呼ばれます。BEGIN_MESSAGE_MAP(CMyWindow, CFrameWnd) ON_WM_PAINT() ON_COMMAND(IDM_END, OnEnd) ON_COMMAND(IDM_ABOUT, OnAbout) END_MESSAGE_MAP()
コンストラクタです。Create関数の6番目の引数にメニュー名を指定します。CMyWindow::CMyWindow() { Create(NULL, "猫でもわかるメニュー", WS_OVERLAPPEDWINDOW, CRect(100, 100, 100+220, 100+120), NULL, "MYMENU"); }
クライアント領域への描画ですが中央に表示するために 少し工夫しています。TextOut()関数の最初の2つの引数は 座標を表しますが、これは、デフォルトではフォントの左上の座標を指定することになります。 そこでSetTextAlign()関数により基準点を変えます。void CMyWindow::OnPaint() { int y; RECT rc; TEXTMETRIC tm; CString str = "粂井康孝・制作"; CPaintDC dc(this); GetClientRect(&rc); dc.GetTextMetrics(&tm); dc.SetTextAlign(TA_CENTER); dc.SetTextColor(RGB(255, 0, 0)); y = (rc.bottom - tm.tmAscent) / 2; dc.TextOut(rc.right / 2, y, str); }
nFlagsで基準点を指定します。TA_CENTERは水平方向の中央、TA_LEFTは左端、TA_RIGHTは右端、 TA_BASELINEは垂直方向でフォントのベースライン、TA_BOTTOMは下端、TA_TOPは上端となります。 デフォルトはTA_TOP | TA_LEFTということになります。CDC::SetTextAlign UINT SetTextAlign( UINT nFlags );
クライアント領域の大きさはGetClientRect関数で調べます。
lpRectはRECT構造体またはCRectオブジェクトへのポインタです。CWnd::GetClientRect void GetClientRect( LPRECT lpRect ) const;
フォントの大きさを知るためにGetTextMetrics()関数を使いました。
lpMetricsはTEXTMETRIC構造体へのポインタです。この構造体の メンバは山ほどあってとても全部は解説できません。ここではtmAscentメンバを フォントの高さとして扱いました。クライアント領域の高さからフォントの高さを ひいて2で割るとフォントの左上のy座標をどこに持ってくれば良いかがわかります。 x座標についてはTA_CENTERを指定してクライアント領域の幅の半分にすれば 良いですね。CDC::GetTextMetrics BOOL GetTextMetrics( LPTEXTMETRIC lpMetrics ) const;
メニューからIDM_ABOUTが選択されたとき実行される関数です。 単にメッセージボックスを出しているだけです。void CMyWindow::OnAbout() { MessageBox("粂井康孝・制作著作", "About"); }
SDKのメッセージボックスと同じ使い方です。ヘルプによると この関数よりAfxMessageBox()関数を推奨しています。CWnd::MessageBox int MessageBox( LPCTSTR lpszText, LPCTSTR lpszCaption = NULL, UINT nType = MB_OK );
メニューからIDM_ENDが選択されたときに実行される関数です。 単にWM_CLOSEメッセージを送ってアプリケーションを終了させています。void CMyWindow::OnEnd() { SendMessage(WM_CLOSE); }
wParamとlParamを省略すると0が代入されます。CWnd::SendMessage LRESULT SendMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );
さて、今回は簡単でした。新しいメンバ関数もいろいろ出てきましたが どれもAPI関数から使い方が類推されます。API関数の第一引数がない形が多いですね。 また、デフォルト引数も多く少しだけ楽ができます。
Update 12/Jan/1998 By Y.Kumei