ホーム ATL/WTL
プロパティシート
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2004/04/27
PR
 WTLでは、プロパティシートとそのプロパティシート上に表示するタブ(プロパティページ)を、 それぞれCPropertySheetImplCPropertyPageImpl というテンプレートクラスでカプセル化しています。 これらのクラスはATLのCDialogImplクラスのように基底クラスとしてのみ使用します。 atldlgs.hヘッダではそれぞれの派生クラスとして、 CPropertySheetクラスとCPropertyPageクラスが定義されています。

// atldlgs.h内
class CPropertySheet : public CPropertySheetImpl<CPropertySheet>
{
    ...
    ...

template <WORD t_wDlgTemplateID>
class CPropertyPage : public CPropertyPageImpl<CPropertyPage<t_wDlgTemplateID> >
{
    ...
    ...
			

 WTLのCPropertySheetクラスとCPropertyPageクラスは、 MFCの同名のクラスとほぼ同じメンバ関数を用意しています。 以下に示すのは、CPropertySheetクラスとCPropertyPageクラスを使用する簡単な例です。 ダイアログリソースをプロパティページに関連付け、 そのページをプロパティシートに追加して表示するだけです。 なお、WTLのプロパティシート関連クラスを使用するためにはatldlgs.hヘッダをインクルードする必要があります。


CPropertyPage<IDD_PROPPAGE_VERSION> page;

CPropertySheet sheet(_T("サンプル"));
sheet.AddPage(page);

if(sheet.DoModal() == IDOK){
    // [OK]ボタンを押して閉じた時の処理
}
			

まずプロジェクトにプロパティページ用のダイアログリソース(IDD_PROPPAGE_MEDIUM) を追加し、IDをIDD_PROPPAGE_VERSIONに変更します。 そしてそのダイアログリソースにスタティックコントロールを配置し、 文字列やアイコンを設定します。 このダイアログリソースをCPropertyPageクラスのテンプレート引数に指定することで、 プロパティページにダイアログリソースが関連付けられます。 プロパティページのタブ部分にはダイアログリソースのタイトル文字列が表示されます。 なお、CPropertyPageクラスのコンストラクタに文字列を指定すると、 その文字列がタブ部分の文字列として設定されます。

次にCPropertySheetクラスのインスタンスを作成し、 AddPage()によってプロパティページをプロパティシートに追加します。 プロパティシートにはAddPage()を複数回呼び出すことによって複数のプロパティページを追加できます。 CPropertySheetクラスの場合もコンストラクタに文字列を指定することによって、 プロパティシートのタイトル文字列を設定することができます。

プロパティシートをモーダル表示するためにはDoModal()を使用します。 [OK]ボタンを押してファイル選択ダイアログを閉じた場合は、 戻り値としてIDOKが、 [キャンセル]を押して閉じた場合はIDCANCELが返ってきます。 なお、Create()を使用するとプロパティシートをモードレス表示できます。

 上の例では単にダイアログリソース上にスタティックコントロールを配置して、 文字列やアイコン表示するだけでした。 しかし、プロパティシートの実際の用途は、 ダイアログリソース上のいろいろなコントロールにデータを設定したり、 そのデータを外部のクラスとやりとりすることでしょう。 そこで、次はCPropertySheetImplクラスやCPropertyPageImplクラスから派生クラスを作成し、 プロパティシートの動作をカスタマイズする例を示します。


まずプロジェクトにプロパティページ用のダイアログリソース(IDD_PROPPAGE_MEDIUM)を追加し、 IDをIDD_PROPPAGE_WINDOWに変更します。 このダイアログリソースにはIDがIDC_CHECK_TOPMOSTのチェックボックスを追加します。

次に、CustomSheet.hというヘッダファイルを用意し、 そこにCCustomPageというプロパティページクラスを定義します。

// CustomSheet.h内
class CCustomPage : public CPropertyPageImpl<CCustomPage>
{
    CButton m_button_topmost;

public:
    enum { IDD = IDD_PROPPAGE_WINDOW };

    bool m_bTopmost;

    // コンストラクタ
    CCustomPage(ATL::_U_STRINGorID title = (LPCTSTR)NULL)
        : CPropertyPageImpl<CCustomPage>(title)
    {}

    BEGIN_MSG_MAP_EX(CCustomPage)
        MSG_WM_INITDIALOG(OnInitDialog)
        CHAIN_MSG_MAP(CPropertyPageImpl<CCustomPage>)
    END_MSG_MAP()

    LRESULT OnInitDialog(HWND hWnd, LPARAM lParam){
        m_button_topmost = GetDlgItem(IDC_CHECK_TOPMOST);

        m_button_topmost.SetCheck(m_bTopmost);

        return TRUE;
    }

    BOOL OnKillActive(){
        m_bTopmost = (m_button_topmost.GetCheck() != 0);

        return TRUE;
    }
};
			

CCustomPageクラスはCPropertyPageImplクラスから派生しており、 (テンプレート引数ではなく)enumによって直接ダイアログリソース (IDD_PROPPAGE_WINDOW)を関連付けています。 また、チェックボックスのチェック状態を表すm_bTopmostというbool型メンバ変数を持っており、 この変数を使って外部のクラスとデータのやりとりをします。 CCustomPageクラスのWM_INITDIALOGメッセージハンドラではこの変数の値でチェックボックスの状態を初期化し、 OnKillActive()内でチェックボックスの状態をm_bTopmostに保存します。 OnKillActive()はプロパティページがフォーカスを失う時 ([OK]ボタンを押してプロパティシートを閉じる時も含む)に呼び出されます。 つまり、プロパティシート(プロパティページ)を表示する前にm_bTopmostに値を設定し、 プロパティシート(プロパティページ)を閉じた後にm_bTopmostの値を取得することによって、 プロパティシートと外部のウィンドウがチェックボックスの状態を共有することができます。

 なお、CCustomPageクラスのメッセージマップでは、 基底クラスであるCPropertyPageImplクラスへのチェーンを追加しています。 CPropertyPageImplクラスはメッセージマップを用意しており、 これはプロパティページに対するWM_NOTIFYメッセージをマッピングします。 WM_NOTIFYメッセージハンドラでは通知コードによって次のようなメンバ関数を呼び出します。

通知コード メンバ関数名
PSN_SETACTIVE OnSetActive
PSN_KILLACTIVE OnKillActive
PSN_APPLY OnApply
PSN_RESET OnReset
PSN_QUERYCANCEL OnQueryCancel
PSN_WIZNEXT OnWizardNext
PSN_WIZBACK OnWizardBack
PSN_WIZFINISH OnWizardFinish
PSN_HELP OnHelp
PSN_GETOBJECT OnGetObject
PSN_TRANSLATEACCELERATOR OnTranslateAccelerator
PSN_QUERYINITIALFOCUS OnQueryInitialFocus

CPropertyPageImplクラスの派生クラスは、これらのメンバ関数をオーバーライドすることによって、 プロパティページの動作をカスタマイズすることができます。

 次に、同じCustomSheet.hファイル内にCCustomSheetというプロパティシートクラスを定義します。

// CustomSheet.h内
class CCustomPage : public CPropertyPageImpl<CCustomPage>
{
    ...
};

class CCustomSheet : public CPropertySheetImpl<CCustomSheet>
{
public:
    CCustomPage m_pageCustom;
    CPropertyPage<IDD_PROPPAGE_VERSION> m_pageVersion;

    // コンストラクタ
    CCustomSheet(ATL::_U_STRINGorID title = (LPCTSTR)NULL,
        UINT uStartPage = 0, HWND hWndParent = NULL)
        : CPropertySheetImpl<CCustomSheet>(title, uStartPage, hWndParent)
    {
        m_psh.dwFlags |= PSH_NOAPPLYNOW;

        AddPage(m_pageCustom);
        AddPage(m_pageVersion);
    }

    BEGIN_MSG_MAP_EX(CCustomSheet)
        CHAIN_MSG_MAP(CPropertySheetImpl<CCustomSheet>)
    END_MSG_MAP()

    static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam){
        if(uMsg == PSCB_PRECREATE){
            // コンテキストヘルプボタンを削除
            ((LPDLGTEMPLATE)lParam)->style &= ~DS_CONTEXTHELP;

            return 0;
        }

        return CPropertySheetImpl<CCustomSheet>::PropSheetCallback(hWnd, uMsg, lParam);
    }
};
			

CCustomSheetクラスのコンストラクタ引数は、 CPropertySheetクラスのコンストラクタ引数を参考にしています。 CCustomSheetクラスのコンストラクタでは、 プロパティシートの[適用]ボタンを非表示にするためにPSH_NOAPPLYNOWフラグを追加しています。 また、メンバ変数として宣言した2つのプロパティページをAddPage()によって追加しています。 CCustomSheetクラスではプロパティシートのコンテキストヘルプボタン (タイトルバー上の[?]ボタン)を非表示にするために、 PropSheetCallback()コールバック関数をオーバーライドしています。 この関数ではPSCB_PRECREATEメッセージが送られてきた時に DS_CONTEXTHELPスタイルを削除しています。 なお、システムにIE5以上がインストールされている場合は、 コンストラクタでPSH_NOCONTEXTHELPフラグを追加することでコンテキストヘルプボタンを非表示にできます。

 次に示すのは、CCustomSheetクラスを使用する例です。

// stdafx.h内
#include <atlbase.h>
#include <atlapp.h>
extern CAppModule _Module;
#include <atlwin.h>

#include <atlcrack.h>
#include <atlmisc.h>
#include <atlctrls.h>
#include <atldlgs.h>  // コモンダイアログを使用するため
			

// maindlg.h内
class CMainDlg : public CDialogImpl<CMainDlg>
{
public:
    enum { IDD = IDD_MAINDLG };

    bool m_bTopmost;

    // メッセージマップ
    BEGIN_MSG_MAP_EX(CMainDlg)
        MSG_WM_INITDIALOG(OnInitDialog)
        COMMAND_ID_HANDLER_EX(IDC_BUTTON_OPENDLG, OnButtonOpenDlg)
        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(IDR_MAINFRAME, LR_DEFAULTCOLOR,
            ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
        SetIcon(hIcon, TRUE);
        
        // 小さいアイコン設定
        HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR,
            ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON));
        SetIcon(hIconSmall, FALSE);

        m_bTopmost = false;

        return TRUE;
    }

    void OnButtonOpenDlg(UINT uNotifyCode, int nID, HWND hWndCtl){
        CCustomSheet sheet(_T("サンプル"));
        sheet.m_pageCustom.m_bTopmost = m_bTopmost;

        if(sheet.DoModal() == IDOK){
            m_bTopmost = sheet.m_pageCustom.m_bTopmost;

            SetWindowPos(m_bTopmost ? 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);
    }
};
			

// CommDlg.cpp内
#include "stdafx.h"

#include "resource.h"

#include "CustomSheet.h"
#include "maindlg.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: アプリケーション実行
    {
        CMainDlg dlgMain;
        nRet = dlgMain.DoModal();
    }

    _Module.Term();
    ::CoUninitialize();

    return nRet;
}
			

 まず、リソースを作成します。ダイアログにボタンコントロールを配置し、 リソースIDを次のように指定します。

コントロール名 リソースID
プッシュボタン IDC_BUTTON_OPENDLG

 次に、stdafx.h内では、atldlgs.hヘッダをインクルードします。

 CMainDlgクラスでは、ボタン用のコマンドメッセージハンドラ OnButtonOpenDlg()を追加し、 そこでCCustomSheetクラスのインスタンスを作成して DoModal()によって独自のプロパティシートを表示します。

 最後に、CommDlg.cpp内でmaindlg.hヘッダの前にCustomSheet.hヘッダをインクルードします。