はじめに
前回は、ATLでウィンドウを作成しました。今回はモーダルダイアログ、モードレスダイアログ、さらにコントロールについて説明します。
対象読者
ATL/WTLによるWindowsプログラミングに興味があり、C++やWin32APIによるWindowsプログラミングの基本的な知識がある方。
必要な環境
サンプルはVisual C++ 6.0で作成し、Windows 2000で動作確認しています。
ATLモーダルダイアログ
ウィンドウを作成するためにはATLのCWindowImpl
クラスを使用しましたが、ダイアログを作成するためにはATLのCDialogImpl
クラスを使用します。
以下に示すのは、[Hello]ボタンを押すと「Hello, ATL/WTL」というメッセージボックスを表示するだけの、簡単なプログラムのソースコードです。このプログラムでは、CDialogImpl
クラスから派生クラスCMainDlg
(これがメインウィンドウとなります)を作り、CMainDlg
クラス内でWM_INITDIALOG
と各ボタンメッセージへの応答を定義しています。なお、このプログラムはATL/WTL AppWizardを使用せずWin32Applicationプロジェクトでビルドします。
#include <atlbase.h> #include <atlapp.h> extern CAppModule _Module; #include <atlwin.h> #include <atlcrack.h> #include <atlmisc.h>
class CMainDlg : public CDialogImpl<CMainDlg> { public: enum { IDD = IDD_DIALOG1 }; // メッセージマップ BEGIN_MSG_MAP_EX(CMainDlg) MSG_WM_INITDIALOG(OnInitDialog) COMMAND_ID_HANDLER_EX(IDC_BUTTON_HELLO, OnHello) COMMAND_ID_HANDLER_EX(IDOK, OnOK) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) END_MSG_MAP() LRESULT OnInitDialog(HWND hWnd, LPARAM lParam){ // スクリーンの中央に配置 CenterWindow(); // 大きいアイコン設定 HICON hIcon = AtlLoadIconImage(IDI_ICON1, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON)); SetIcon(hIcon, TRUE); // 小さいアイコン設定 HICON hIconSmall = AtlLoadIconImage(IDI_ICON1, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON)); SetIcon(hIconSmall, FALSE); return TRUE; } // [Hello]ボタンハンドラ void OnHello(UINT uNotifyCode, int nID, HWND hWndCtl){ MessageBox(_T("Hello, ATL/WTL")); } // [OK]ボタンハンドラ void OnOK(UINT uNotifyCode, int nID, HWND hWndCtl){ EndDialog(nID); } // [キャンセル]ボタンハンドラ void OnCancel(UINT uNotifyCode, int nID, HWND hWndCtl){ EndDialog(nID); } };
#include "stdafx.h" #include "resource.h" #include "MainDlg.h" CAppModule _Module; int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow) { _Module.Init(NULL, hInstance); // モーダルダイアログ表示 CMainDlg dlgMain; int nRet = dlgMain.DoModal(); _Module.Term(); return nRet; }
まずはプロジェクトにダイアログリソースとアイコンリソースを追加し、IDを次のように設定します。
リソース名 | リソースID | 説明 |
ダイアログ | IDD_DIALOG1 | メインウィンドウ |
アイコン | IDI_ICON1 | メインアイコン |
さらにダイアログリソースにはボタンを一つ配置し、IDを次のように設定します。
コントロール名 | リソースID | 説明 |
ボタン | IDC_BUTTON_HELLO | [Hello]ボタン |
次に、前回のウィンドウ作成の時と同様、CAppModule
クラスのインスタンスをグローバルに宣言し、_tWinMain()
の最初と最後で初期化と後始末をしています。モーダルダイアログを作成するには、DoModal()
を呼び出します。
CMainDlg
クラスはCDialogImpl
クラスから派生していますが、CDialogImpl
の第1テンプレート引数にもCMainDlgという名前を渡します(第2テンプレート引数は省略可能です。ここでは省略しています)。CMainDlg
クラス内では、public
宣言のenum
によってダイアログリソースIDを定義し、メッセージマップによってメッセージとそれに対するハンドラを関連付けています。
WM_INITDIALOG
メッセージハンドラ関数であるOnInitDialog
では、ダイアログをスクリーン中央に配置し、アイコンを設定します。
[Hello]ボタン(リソースID:IDC_BUTTON_HELLO
)を押したときのハンドラ関数はOnHello
です。この関数ではメッセージボックスを表示するだけです。
[OK]ボタン(リソースID:IDOK
)または[キャンセル]ボタン(リソースID:IDCANCEL
)を押すと、プログラムを終了するようにしています。今回の例ではモーダルダイアログがメインウィンドウとなるので、ボタンを押した時にEndDialog()
を呼び出してダイアログを閉じています。
ATLモードレスダイアログ
モードレスダイアログを作成する場合も、モーダルダイアログと同様にCDialogImpl
クラスを使用します。以下に示すのは、前述のモーダルダイアログ版「Hello, ATL/WTL」プログラムを、モードレスダイアログに変更したプログラムのソースコードです。変更するのは「MainDlg.h」ファイルと「hello.cpp」ファイルです。
class CMainDlg : public CDialogImpl<CMainDlg>, public CMessageFilter, public CIdleHandler { public: enum { IDD = IDD_DIALOG1 }; // メッセージフィルタ処理 virtual BOOL PreTranslateMessage(MSG* pMsg){ return IsDialogMessage(pMsg); } // アイドル処理 virtual BOOL OnIdle(){ return FALSE; } // メッセージマップ BEGIN_MSG_MAP_EX(CMainDlg) MSG_WM_INITDIALOG(OnInitDialog) COMMAND_ID_HANDLER_EX(IDC_BUTTON_HELLO, OnHello) COMMAND_ID_HANDLER_EX(IDOK, OnOK) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) END_MSG_MAP() LRESULT OnInitDialog(HWND hWnd, LPARAM lParam){ // スクリーンの中央に配置 CenterWindow(); // 大きいアイコン設定 HICON hIcon = AtlLoadIconImage(IDI_ICON1, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON)); SetIcon(hIcon, TRUE); // 小さいアイコン設定 HICON hIconSmall = AtlLoadIconImage(IDI_ICON1, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON)); SetIcon(hIconSmall, FALSE); // メッセージループにメッセージフィルタとアイドルハンドラを追加 CMessageLoop* pLoop = _Module.GetMessageLoop(); pLoop->AddMessageFilter(this); pLoop->AddIdleHandler(this); return TRUE; } // [Hello]ボタンハンドラ void OnHello(UINT uNotifyCode, int nID, HWND hWndCtl){ MessageBox(_T("Hello, ATL/WTL")); } // [OK]ボタンハンドラ void OnOK(UINT uNotifyCode, int nID, HWND hWndCtl){ DestroyWindow(); ::PostQuitMessage(nID); } // [キャンセル]ボタンハンドラ void OnCancel(UINT uNotifyCode, int nID, HWND hWndCtl){ DestroyWindow(); ::PostQuitMessage(nID); } };
#include "stdafx.h" #include "resource.h" #include "MainDlg.h" CAppModule _Module; int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow) { _Module.Init(NULL, hInstance); CMessageLoop theLoop; _Module.AddMessageLoop(&theLoop); // モードレスダイアログ表示 CMainDlg dlgMain; dlgMain.Create(NULL); dlgMain.ShowWindow(nCmdShow); int nRet = theLoop.Run(); _Module.RemoveMessageLoop(); _Module.Term(); return nRet; }
モードレスダイアログとモーダルダイアログでは、主に以下の点が異なります。
ダイアログの作成
モーダルダイアログでは_tWinMain()
内でDoModal()
を呼び出しましたが、モードレスダイアログはCreate()
を呼び出して作成します。
メッセージループの管理
モードレスダイアログでは、メッセージループをCMessageLoop
クラスで管理できるため、CMessageFilter
クラスやCIdleHandler
クラスを使ってメッセージフィルタやアイドルハンドラを利用できます。今回の例ではCMainDlg
クラスの基底クラスにCMessageFilter
クラスやCIdleHandler
クラスを追加し、CMainDlg
クラス内でメッセージフィルタハンドラ関数(PreTranslateMessage
)とアイドルハンドラ関数(OnIdle
)を定義しています。
ダイアログの閉じ方
モーダルダイアログでは、[OK]ボタンハンドラと[キャンセル]ボタンハンドラでEndDialog()
を呼び出しましたが、モードレスダイアログはDestroyWindow()
と::PostQuitMessage()
を呼び出して閉じます。
コントロール
WTLは、Windowsの標準コントロールやコモンコントロールをサポートするクラスを用意しています。それらはMFCとメンバ関数名が同じものも多く、MFCに慣れた開発者には扱いやすいものとなっています。
WTLの標準コントロールやコモンコントロールは、「atlctrls.h」ファイルで主に次のように定義されています。
template <class TBase> class CButtonT : public TBase { // ...(省略)... }; typedef CButtonT<ATL::CWindow> CButton;
つまり、WTLのコントロールはATL::CWindow
の派生クラスということになります。
コントロールを使ったサンプル:カラーピッカー
以下に示すのは、コントロールを使ったカラーピッカープログラムです。スポイトボタンを押してからデスクトップ上の任意の点をクリックすると、その点の色がスポイトボタンの右側に表示されます。また、HTML用のカラーコードがエディットコントロールに表示され、コピーボタンを押すとそのカラーコードがクリップボードにコピーされます。
#include <atlbase.h> #include <atlapp.h> extern CAppModule _Module; #include <atlwin.h> #include <atlcrack.h> #include <atlmisc.h> #include <atlctrls.h> // コントロール用クラスを使用するため
class CMainDlg : public CDialogImpl<CMainDlg> { public: enum { IDD = IDD_DIALOG_MAIN }; CButton m_button_spuit; // [スポイト]ラジオボタン CButton m_button_copy; // [コピー]ラジオボタン CStatic m_static_color; // 色表示エリア CEdit m_edit_color; // 色値 CButton m_check_topmost; // [常に手前に表示]チェックボックス bool m_bClick; // スポイトボタンがクリックされたか // どうかを示すフラグ COLORREF m_color; // 取得した色値 // メッセージマップ BEGIN_MSG_MAP_EX(CMainDlg) MSG_WM_INITDIALOG(OnInitDialog) MSG_WM_PAINT(OnPaint) MSG_WM_LBUTTONDOWN(OnLButtonDown) COMMAND_ID_HANDLER_EX(IDC_BUTTON_SPUIT, OnButtonSpuit) COMMAND_ID_HANDLER_EX(IDC_BUTTON_COPY, OnButtonCopy) COMMAND_ID_HANDLER_EX(IDC_CHECK_TOPMOST, OnCheckTopmost) COMMAND_ID_HANDLER_EX(IDOK, OnOK) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) END_MSG_MAP() LRESULT OnInitDialog(HWND hWnd, LPARAM lParam){ // スクリーンの中央に配置 CenterWindow(); // 大きいアイコン設定 HICON hIcon = AtlLoadIconImage(IDI_ICON_MAIN, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON)); SetIcon(hIcon, TRUE); // 小さいアイコン設定 HICON hIconSmall = AtlLoadIconImage(IDI_ICON_MAIN, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON)); SetIcon(hIconSmall, FALSE); // コントロール設定 m_button_spuit = GetDlgItem(IDC_BUTTON_SPUIT); m_button_copy = GetDlgItem(IDC_BUTTON_COPY); m_static_color = GetDlgItem(IDC_STATIC_COLOR); m_edit_color = GetDlgItem(IDC_EDIT_COLOR); m_check_topmost = GetDlgItem(IDC_CHECK_TOPMOST); // スポイトボタンにアイコンを設定 HICON hIconSpuit = AtlLoadIconImage(IDI_ICON_SPUIT, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON)); m_button_spuit.SetIcon(hIconSpuit); // コピーボタンにアイコンを設定 HICON hIconCopy = AtlLoadIconImage(IDI_ICON_COPY, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON)); m_button_copy.SetIcon(hIconCopy); // ツールチップコントロール作成 CToolTipCtrl tooltip; tooltip.Create(m_hWnd); tooltip.Activate(TRUE); // ボタンコントロールのツールチップ情報を // ツールチップコントロールに追加 CToolInfo tiSpuit(TTF_SUBCLASS, m_button_spuit, 0, NULL, _T("スポイト")); tooltip.AddTool(tiSpuit); CToolInfo tiCopy(TTF_SUBCLASS, m_button_copy, 0, NULL, _T("コピー")); tooltip.AddTool(tiCopy); m_bClick = false; m_color = RGB(255, 255, 255); return TRUE; } void OnPaint(HDC /*hDC*/){ CPaintDC dc(m_hWnd); // スタティックコントロールの矩形に色を表示 CRect rcColor; m_static_color.GetWindowRect(rcColor); ScreenToClient(rcColor); dc.FillSolidRect(rcColor, m_color); } void OnLButtonDown(UINT nFlags, CPoint point){ if(m_bClick){ // クリックした位置の色を取得 ClientToScreen(&point); CClientDC dc(NULL); m_color = dc.GetPixel(point); ReleaseCapture(); m_bClick = false; // エディットコントロールに色値を設定 CString strColor; strColor.Format(_T("#%02x%02x%02x"), GetRValue(m_color), GetGValue(m_color), GetBValue(m_color)); m_edit_color.SetWindowText(strColor); // 色表示エリアを更新 CRect rcColor; m_static_color.GetWindowRect(rcColor); ScreenToClient(rcColor); InvalidateRect(rcColor); } } void OnButtonSpuit(UINT uNotifyCode, int nID, HWND hWndCtl){ m_bClick = true; SetCapture(); } void OnButtonCopy(UINT uNotifyCode, int nID, HWND hWndCtl){ m_edit_color.SetSel(0,-1); //エディットボックス内を全て選択する m_edit_color.Copy(); //選択したものをコピーする } void OnCheckTopmost(UINT uNotifyCode, int nID, HWND hWndCtl){ SetWindowPos(m_check_topmost.GetCheck() ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW); } void OnOK(UINT uNotifyCode, int nID, HWND hWndCtl){ EndDialog(nID); } void OnCancel(UINT uNotifyCode, int nID, HWND hWndCtl){ EndDialog(nID); } };
#include "stdafx.h" #include "resource.h" #include "MainDlg.h" CAppModule _Module; int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow) { _Module.Init(NULL, hInstance); CMainDlg dlgMain; int nRet = dlgMain.DoModal(); _Module.Term(); return nRet; }
まずは、プロジェクトにダイアログリソースとアイコンリソースを追加し、IDを次のように設定します。
リソース名 | リソースID | プロパティ | 説明 |
ダイアログ | IDD_DIALOG_MAIN | [最小化ボタン]にチェックを入れる | メインダイアログ |
アイコン | IDI_ICON_MAIN | (デフォルト) | メインアイコン |
アイコン | IDI_ICON_SPUIT | (デフォルト) | スポイトアイコン |
アイコン | IDI_ICON_COPY | (デフォルト) | コピーアイコン |
さらにダイアログリソースにはコントロールを配置し、IDを次のように設定します。
コントロール名 | リソースID | プロパティ | 説明 |
ボタン | IDC_BUTTON_SPUIT | [アイコン]にチェックを入れる | [スポイト]ボタン |
ボタン | IDC_BUTTON_COPY | [アイコン]にチェックを入れる | [コピー]ボタン |
スタティック | IDC_STATIC_COLOR | [可視]のチェックをはずす | 色表示エリア |
エディット | IDC_EDIT_COLOR | (デフォルト) | 色値 |
チェック | IDC_CHECK_TOPMOST | (デフォルト) | [常に手前に表示する]チェックボタン |
CMainDlg
クラスでは、まずコントロールのインスタンスをメンバ変数として宣言します。これらのインスタンスは、OnInitDialog()
内でGetDlgItem()
を呼び出すことによって、コントロールのリソースIDと関連付けています。OnInitDialog()
ではさらに、スポイトボタンとコピーボタンにアイコンとツールチップを設定します。
次に、スポイトボタンのハンドラ関数OnButtonSpuit()
でフラグをセットし、SetCapture()
を呼び出します。これにより、このウィンドウ外でクリックしても、マウスメッセージを受け取ることができます。
実際にスポイトボタンを押した後に任意の場所をクリックすると、OnLButtonDown()
が呼び出されます。この関数ではクリックした位置の色を取得し、エディットコントロールに色値を設定してから色表示エリアを更新します。実際に色を描画するのはOnPaint()
内です。
最後に
本稿では、ATL/WTLでダイアログウィンドウとコントロールを作成しました。次回は、フレームウィンドウとビューを扱う予定です。