メインウィンドウ
以下に、前述の二つのビューウィンドウを使用するプログラム全体のソースコードを示します。
#include <atlbase.h> #include <atlapp.h> extern CAppModule _Module; #include <atlwin.h> #include <atlcrack.h> #include <atlmisc.h> #include <atlctrls.h> #include <atlctrlw.h> // コマンドバーを使用するため #include <atlframe.h> #include <atlsplit.h> // スプリッタウィンドウを使用するため
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>, public CMessageFilter, public CIdleHandler { public: DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME) CSplitterWindow m_wndSplitter; // スプリッタウィンドウ CFontListView m_viewFontList; // 左ペイン CFontPreviewView m_viewFontPreview; // 右ペイン CCommandBarCtrl m_CmdBar; // コマンドバー virtual BOOL PreTranslateMessage(MSG* pMsg){ if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg)) return TRUE; // 左ペインのPreTranslateMessageを呼び出す if(m_viewFontList.PreTranslateMessage(pMsg)) return TRUE; // 右ペインのPreTranslateMessageを呼び出す return m_viewFontPreview.PreTranslateMessage(pMsg); } virtual BOOL OnIdle(){ UIUpdateToolBar(); UIUpdateStatusBar(); CString strCount; strCount.Format(_T("フォント数:%d"), m_viewFontList.GetCount()); CStatusBarCtrl bar = m_hWndStatusBar; bar.SetText(1, strCount); return FALSE; } BEGIN_UPDATE_UI_MAP(CMainFrame) UPDATE_ELEMENT(ID_MENUITEM_CHANGEVIEW, UPDUI_MENUPOPUP | UPDUI_TOOLBAR) END_UPDATE_UI_MAP() BEGIN_MSG_MAP_EX(CMainFrame) MSG_WM_CREATE(OnCreate) MSG_WM_SIZE(OnSize) COMMAND_CODE_HANDLER_EX(LBN_SELCHANGE, OnListSelChange) COMMAND_ID_HANDLER_EX(ID_MENUITEM_CHANGEVIEW, OnMenuChangeView) COMMAND_ID_HANDLER_EX(ID_APP_EXIT, OnFileExit) CHAIN_MSG_MAP(CUpdateUI<CMainFrame>) CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>) END_MSG_MAP() LRESULT OnCreate(LPCREATESTRUCT lpcs){ // コマンドバーを作成 HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE); // コマンドバーに現在のメニューバーのアイテムと画像をセット m_CmdBar.AttachMenu(GetMenu()); m_CmdBar.LoadImages(IDR_MAINFRAME); // メニューバーを削除 SetMenu(NULL); // ツールバーを作成 HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE); // リバーを作成 CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE); // リバーのバンドにコマンドバーとツールバーを追加 AddSimpleReBarBand(hWndCmdBar); AddSimpleReBarBand(hWndToolBar, NULL, TRUE); // ステータスバーを作成 CreateSimpleStatusBar(); UIAddToolBar(hWndToolBar); UIAddStatusBar(m_hWndStatusBar); UISetCheck(ID_MENUITEM_CHANGEVIEW, 1); // スプリッタウィンドウを作成 m_wndSplitter.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); // スプリッタウィンドウ拡張スタイルを設定 m_wndSplitter.SetSplitterExtendedStyle(0); // 左ペインのビューウィンドウを作成 m_viewFontList.Create(m_wndSplitter, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT | LBS_SORT, WS_EX_CLIENTEDGE); m_wndSplitter.SetSplitterPane(SPLIT_PANE_LEFT, m_viewFontList); // 右ペインのビューウィンドウを作成 m_viewFontPreview.Create(m_wndSplitter, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE); m_wndSplitter.SetSplitterPane(SPLIT_PANE_RIGHT, m_viewFontPreview); m_hWndClient = m_wndSplitter; UpdateLayout(); // 分割バーの位置を設定 m_wndSplitter.SetSplitterPos(120); // メッセージループに // メッセージフィルタとアイドルハンドラを追加 CMessageLoop* pLoop = _Module.GetMessageLoop(); pLoop->AddMessageFilter(this); pLoop->AddIdleHandler(this); return 0; } void OnSize(UINT uType, CSize size){ // ステータスバーにペインを設定 if(m_hWndStatusBar != NULL){ CStatusBarCtrl bar = m_hWndStatusBar; // フォント数を表示するペインの幅 int cxPosPane = 80; // デフォルトペインの幅 CRect rcClient; GetClientRect(rcClient); int cxDefaultPane = rcClient.right - cxPosPane - ::GetSystemMetrics(SM_CXVSCROLL) - ::GetSystemMetrics(SM_CXEDGE); int nPanes[] = {cxDefaultPane, cxDefaultPane + cxPosPane}; bar.SetParts(sizeof(nPanes)/sizeof(nPanes[0]), nPanes); } // 基底クラスのWM_SIZEメッセージハンドラも呼び出すため SetMsgHandled(false); } void OnListSelChange(UINT uNotifyCode, int nID, HWND hWndCtl){ // 現在選択されているアイテムを取得 int nIndex = m_viewFontList.GetCurSel(); if(nIndex != LB_ERR){ CString strText; m_viewFontList.GetText(nIndex, strText); // 取得した文字列を右ペインに設定 m_viewFontPreview.SetFontName(strText); } } void OnMenuChangeView(UINT uNotifyCode, int nID, HWND hWndCtl){ if(m_wndSplitter.GetSinglePaneMode() == SPLIT_PANE_RIGHT){ // 両ペイン表示 m_wndSplitter.SetSinglePaneMode(SPLIT_PANE_NONE); UISetCheck(ID_MENUITEM_CHANGEVIEW, 1); }else{ // 右ペインのみ表示 m_wndSplitter.SetSinglePaneMode(SPLIT_PANE_RIGHT); UISetCheck(ID_MENUITEM_CHANGEVIEW, 0); } } void OnFileExit(UINT uNotifyCode, int nID, HWND hWndCtl){ PostMessage(WM_CLOSE); } };
#include "stdafx.h" #include "resource.h" #include "FontListView.h" #include "FontPreviewView.h" #include "MainFrm.h" CAppModule _Module; int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow) { HRESULT hRes = ::CoInitialize(NULL); ATLASSERT(SUCCEEDED(hRes)); ::DefWindowProc(NULL, 0, 0, 0L); AtlInitCommonControls(ICC_COOL_CLASSES | ICC_WIN95_CLASSES); hRes = _Module.Init(NULL, hInstance); ATLASSERT(SUCCEEDED(hRes)); int nRet = 0; // BLOCK: アプリケーション実行 { CMessageLoop theLoop; _Module.AddMessageLoop(&theLoop); CMainFrame wnd; wnd.CreateEx(); wnd.ShowWindow(nCmdShow); wnd.UpdateWindow(); nRet = theLoop.Run(); _Module.RemoveMessageLoop(); } _Module.Term(); :: CoUninitialize(); return nRet; }
まず、リソースを用意します。今回のプログラムでは使用しないアクセラレータリソースとダイアログリソースをプロジェクトから削除します。
メニューリソース(リソースID:IDR_MAINFRAME
)は次のように設定します。
トップレベル | アイテム名 | リソースID |
ファイル(&F) | アプリケーションの終了(&X) | ID_APP_EXIT |
表示(&V) | ビュー切り換え(&C) | ID_MENUITEM_CHANGEVIEW |
![メニューリソース メニューリソース](http://cz-cdn.shoeisha.jp/static/images/article/346/menubar.gif)
ツールバーリソース(リソースID:IDR_MAINFRAME
)にはアイテムを1つ追加し、リソースIDにID_MENUITEM_CHANGEVIEW
を設定します。
![ツールバーリソース ツールバーリソース](http://cz-cdn.shoeisha.jp/static/images/article/346/toolbar.gif)
次に、「stdafx.h」で「atlctrlw.h」と「atlframe.h」、「atlsplit.h」をインクルードします。これらはコマンドバーやUI更新ハンドラ、スプリッタウィンドウを使用するために必要です。
次にCMainFrame
クラスを定義します。本稿では以下の部分に注目して説明します。
コマンドバー
まず、CMainFrame
クラスのメンバ変数としてCCommandBarCtrl
のインスタンスであるm_CmdBar
を宣言します。
CMainFrame
クラスのWM_CREATE
メッセージハンドラでは、m_CmdBar
に対してCreate()
を呼び出すことでコマンドバーを作成します。Create()
の第4引数にはコマンドバーのスタイルを指定します。ATL_SIMPLE_CMDBAR_PANE_STYLE
はコマンドバーの標準的なスタイルを意味し、「atlctrlw.h」ヘッダ内で次のように定義されています。
// Window Styles: #define CBRWS_TOP CCS_TOP #define CBRWS_BOTTOM CCS_BOTTOM #define CBRWS_NORESIZE CCS_NORESIZE #define CBRWS_NOPARENTALIGN CCS_NOPARENTALIGN #define CBRWS_NODIVIDER CCS_NODIVIDER // standard command bar styles #define ATL_SIMPLE_CMDBAR_PANE_STYLE \ (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | \ CBRWS_NODIVIDER | CBRWS_NORESIZE | CBRWS_NOPARENTALIGN)
コマンドバーを作成した後は、コマンドバーにメニューアイテムを追加します。今回のプログラムはAttachMenu()
を呼び出すことによってフレームウィンドウのメニューバーからメニューアイテムを読み込んでいますが、LoadMenu()
を呼び出してメニューリソースから直接アイテムをロードすることもできます。
次に、LoadImages()
を呼び出すことによってツールバーの画像をロードしています。これにより、IDが等しいメニューアイテムとツールバーの画像が関連付けられ、メニューアイテムの横に画像が表示されるようになります。なお、メニューアイテムの横に画像を表示するためには、AddBitmap()
やAddIcon()
を使うこともできます。
最後にツールバーとリバーを作成し、バンドにコマンドバーとツールバーを追加します。
ステータスバー
今回のプログラムは、ステータスバーの右端にフォント数を表示する領域(ペイン)を持ちます。この領域はフレームウィンドウのサイズが変わるたびに設定する必要があるため、CMainFrame
クラスのWM_SIZE
メッセージハンドラで設定します。
WM_SIZE
メッセージハンドラでは、ステータスバーに2つのペインを追加します。1番目はフレームウィンドウの大きさに合わせて幅を調整するデフォルトペイン、2番目はフォント数を表示するペインです。ペインを設定するにはSetParts()
を呼び出します。第1引数にはペインの数、第2引数にはペインの右端の座標を要素とする配列へのポインタを指定します。なお、WM_SIZE
メッセージは基底クラスであるCFrameWindowImpl
クラス内でもマッピングされているため、最後にSetMsgHandled(false)
を呼び出す必要があります。
フォント数はOnIdle()
内で取得してステータスバーに表示します。
スプリッタウィンドウ
まず、CMainFrame
クラスのメンバ変数としてスプリッタウィンドウクラスであるCSplitterWindow
のインスタンスであるm_wndSplitter
を宣言します。さらに、各ペインのクラスであるCFontListView
とCFontPreviewView
のインスタンスもメンバ変数として宣言します。
CMainFrame
クラスのWM_CREATE
メッセージハンドラでは、m_wndSplitter
に対してCreate()
を呼び出すことでスプリッタウィンドウを作成します。
次に、SetSplitterExtendedStyle()
を呼び出してスプリッタウィンドウの拡張スタイルを設定します。今回のプログラムでは0を設定していますが、これは、スプリッタウィンドウのサイズが変更された場合に、左ペインの幅(水平分割スプリッタウィンドウの場合は上ペインの高さ)を固定することを意味します。スプリッタウィンドウ拡張スタイルには次のスタイルが指定できます。
スタイル | 説明 |
SPLIT_PROPORTIONAL (デフォルト) | スプリッタウィンドウのサイズが変更された場合に両ペインのサイズも変更 |
SPLIT_RIGHTALIGNED | スプリッタウィンドウのサイズが変更された場合に右ペインの幅を固定 |
SPLIT_BOTTOMALIGNED | スプリッタウィンドウのサイズが変更された場合に下ペインの幅を固定 |
SPLIT_NONINTERACTIVE | スプリッタウィンドウの分割バーを移動不可 |
スプリッタウィンドウを作成した後は、スプリッタウィンドウを親として各ペインのウィンドウを作成します。各ペインはSetSplitterPane()
を呼び出すことによってスプリッタウィンドウにセットされます。SetSplitterPane()
の第1引数にはペインを識別するための次のようなフラグを指定します。
識別フラグ | 説明 |
SPLIT_PANE_LEFT | 左ペインを識別 |
SPLIT_PANE_RIGHT | 右ペインを識別 |
SPLIT_PANE_TOP | 上ペインを識別 |
SPLIT_PANE_BOTTOM | 下ペインを識別 |
なお、両方のペインを一度に設定する場合はSetSplitterPanes()
が使えます。これは第1引数に左(または上)ペインのウィンドウハンドル、第2引数には右(または下)ペインのウィンドウハンドルを設定します。
WM_CREATE
メッセージハンドラでは、最後にスプリッタウィンドウのハンドルをm_hWndClient
に代入し、UpdateLayout()
を呼び出してレイアウトを更新してからSetSplitterPos()
で分割バーの位置を設定しています。
次に、スプリッタウィンドウに関連する2つのコマンドメッセージハンドラを追加します。1つはフォントリストのアイテムを選択した時に実行されるOnListSelChange()
、もう1つはメニューの[ビュー切り換え]を選択した時に実行されるOnMenuChangeView()
です。
OnListSelChange()
は、LBN_SELCHANGE
通知コードを持つコマンドメッセージのハンドラ関数です。通常、このメッセージはフォントリストペインの親であるスプリッタウィンドウへ送られますが、CSplitterWindow
クラスの基底クラスであるCSplitterWindowImpl
クラス内のメッセージマップにはFORWARD_NOTIFICATIONS()
がエントリされているため、コマンドメッセージはさらにその親であるフレームウィンドウへ送られます(同様に、ペインで通知メッセージWM_NOTIFY
が発生した場合もフレームウィンドウへ送られます)。今回のプログラムではこのハンドラ関数で、フォントリストの選択されているアイテム(フォント名)を取得して右ペインに設定します。
OnMenuChangeView()
では、GetSinglePaneMode()
を呼び出して現在のペインの状態を取得し、SetSinglePaneMode()
を呼び出してペインをセットします。
UI更新ハンドラ
まずはCMainFrame
の基底クラスにCUpdateUI
を追加します。CMainFrame
クラスにはUI更新ハンドラマップを用意し、更新するメニューアイテムのリソースIDとUIのタイプを登録します。
BEGIN_UPDATE_UI_MAP(CMainFrame) UPDATE_ELEMENT(ID_MENUITEM_CHANGEVIEW, UPDUI_MENUPOPUP | UPDUI_TOOLBAR) END_UPDATE_UI_MAP()
今回のプログラムでは、ID_MENUITEM_CHANGEVIEW
をメニューとツールバーに使用しているため、UIのタイプにUPDUI_MENUPOPUPとUPDUI_TOOLBAR
を設定しています。UIのタイプには次のようなものが用意されています。
UIタイプ | 説明 |
UPDUI_MENUPOPUP | ポップアップメニュー(メニューバー下のメニューアイテム含む) |
UPDUI_MENUBAR | メニューバー(トップレベルのメニューアイテム) |
UPDUI_CHILDWINDOW | 子ウィンドウ |
UPDUI_TOOLBAR | ツールバー |
UPDUI_STATUSBAR | ステータスバー |
UI更新ハンドラでは、ポップアップメニューとそれ以外のUIの更新方法が少し異なります。
ポップアップメニュー
メッセージマップに、基底クラスであるCUpdateUI
クラスへチェーンを追加します。CUpdateUI
クラスへチェーンすることによって、メインウィンドウにWM_INITMENUPOPUP
メッセージが送られてくるたびにCUpdateUI
へメッセージが送られます。CUpdateUI
の基底クラスであるCUpdateUIBase
にはWM_INITMENUPOPUP
メッセージハンドラが用意されており、そこでポップアップメニューの状態が更新されます。
このため、UISetCheck()
でポップアップメニューの状態を変更すると、WM_INITMENUPOPUP
メッセージが送られてくるタイミングでポップアップメニューの状態が更新されます。
ポップアップメニュー以外
今回のプログラムではツールバーを例にしますが、まず、CMainFrame
クラスのWM_CREATE
メッセージハンドラでUIAddToolBar()
を呼び出します。UIAddToolBar()
の引数には、更新するツールバーのハンドルを指定します。UI更新ハンドラは、ここで指定されたハンドルで識別されるツールバーに対して更新処理を行います。CUpdateUI
は次のようなハンドル追加用メンバ関数を用意しています。
メンバ関数 | 説明 |
UIAddMenuBar | メニューバー(トップレベルのメニューアイテム)のハンドルを追加 |
UIAddChildWindowContainer | 子ウィンドウのハンドルを追加 |
UIAddToolBar | ツールバーのハンドルを追加 |
UIAddStatusBar | ステータスバーのハンドルを追加 |
次に、OnIdle()
でUIUpdateToolBar()
を呼び出します。UIUpdateToolBar()
は、UI更新ハンドラマップに登録されたツールバーアイテムを更新します。CUpdateUI
は次のような更新用メンバ関数を用意しています。
メンバ関数 | 説明 |
UIUpdateMenuBar | メニューバー(トップレベルのメニューアイテム)を更新 |
UIUpdateChildWindows | 子ウィンドウを更新 |
UIUpdateToolBar | ツールバーを更新 |
UIUpdateStatusBar | ステータスバーを更新 |
このため、UISetCheck()
でツールバーの状態を変更すると、アイドル時にツールバーの状態が更新されます。
最後に
本稿では、スプリッタウィンドウ、コマンドバー、UI更新ハンドラを使用しました。次回は、ペインコンテナやマルチペインステータスバーなどを扱いたいと思います。