Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

ATL/WTLプログラミング 2:ダイアログとコントロール

ATL/WTLを利用したVisual C++のWindowsプログラミング

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2005/11/01 12:00

Windowsアプリケーションを作成するためのC++クラスライブラリといえば、Microsoftが提供するMFCが有名ですが、同社が提供するライブラリATLを利用して作成することもできます。本稿では、ATL/WTLにおけるダイアログとコントロールの利用方法について解説します。

はじめに

 前回は、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プロジェクトでビルドします。

モーダルダイアログ版「Hello, ATL/WTL」プログラム
モーダルダイアログ版「Hello, ATL/WTL」プログラム
stdafx.h
#include <atlbase.h>
#include <atlapp.h>
extern CAppModule _Module;
#include <atlwin.h>

#include <atlcrack.h>
#include <atlmisc.h>
MainDlg.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);
    }
};
hello.cpp
#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」ファイルです。

MainDlg.h
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);
    }
};
hello.cpp
#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」ファイルで主に次のように定義されています。

CButtonの定義
template <class TBase>
class CButtonT : public TBase
{
    // ...(省略)...
};

typedef CButtonT<ATL::CWindow>   CButton;

 つまり、WTLのコントロールはATL::CWindowの派生クラスということになります。

コントロールを使ったサンプル:カラーピッカー

 以下に示すのは、コントロールを使ったカラーピッカープログラムです。スポイトボタンを押してからデスクトップ上の任意の点をクリックすると、その点の色がスポイトボタンの右側に表示されます。また、HTML用のカラーコードがエディットコントロールに表示され、コピーボタンを押すとそのカラーコードがクリップボードにコピーされます。

WTL Color Picker
WTL Color Picker
stdafx.h
#include <atlbase.h>
#include <atlapp.h>
extern CAppModule _Module;
#include <atlwin.h>

#include <atlcrack.h>
#include <atlmisc.h>
#include <atlctrls.h>  // コントロール用クラスを使用するため
MainDlg.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);
    }
};
proj02.cpp
#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でダイアログウィンドウとコントロールを作成しました。次回は、フレームウィンドウとビューを扱う予定です。

参考資料

  1. Windows Template Library (WTL) 7.1
  2. Windows Template Library (WTL)
  3. WTL support list
  4. The Code Project


  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

バックナンバー

連載:ATL/WTLを利用したVisual C++のWindowsプログラミング
All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5