ホーム ATL/WTL
検索置換ダイアログ
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2004/04/20
PR
 WTLではコモンダイアログの検索ダイアログと置換ダイアログを CFindReplaceDialogImplというテンプレートクラスでカプセル化しています。 このクラスはATLのCDialogImplクラスのように基底クラスとしてのみ使用します。 atldlgs.hヘッダではCFindReplaceDialogImplクラスの派生クラスとして、 CFindReplaceDialogクラスが定義されています。

// atldlgs.h内
class CFindReplaceDialog : public CFindReplaceDialogImpl<CFindReplaceDialog>
{
    ...
    ...
			

 WTLのCFindReplaceDialogクラスは、MFCの同名のクラスとほぼ同じメンバ関数を用意しています。 以下に示すのは、CFindReplaceDialogクラスを使用して検索ダイアログと置換ダイアログを作成する例です。 ダイアログ上のエディットコントロールに入力された文字列に対し、検索や置換を実行します。



// 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 };

    enum { MAX_LINE = 256};
    CEdit m_edit_line;

    bool m_bOpen;

    // コンストラクタ
    CMainDlg() : m_bOpen(false)
    {}

    // メッセージマップ
    BEGIN_MSG_MAP_EX(CMainDlg)
        MESSAGE_HANDLER_EX(CFindReplaceDialog::GetFindReplaceMsg(), OnFindReplace)
        MSG_WM_INITDIALOG(OnInitDialog)
        COMMAND_ID_HANDLER_EX(IDC_BUTTON_FINDDLG, OnButtonFindDlg)
        COMMAND_ID_HANDLER_EX(IDC_BUTTON_REPLACEDLG, OnButtonReplaceDlg)
        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_edit_line = GetDlgItem(IDC_EDIT_LINE);

        m_edit_line.LimitText(MAX_LINE - 1);

        return TRUE;
    }

    void OnButtonFindDlg(UINT uNotifyCode, int nID, HWND hWndCtl){
        OpenFindReplaceDlg(TRUE);
    }

    void OnButtonReplaceDlg(UINT uNotifyCode, int nID, HWND hWndCtl){
        OpenFindReplaceDlg(FALSE);
    }

    void OpenFindReplaceDlg(BOOL bFind){
        if(m_bOpen)
            return;
        m_bOpen = true;

        CFindReplaceDialog* pDlg = new CFindReplaceDialog;
        pDlg->Create(bFind, NULL, NULL,
            FR_HIDEWHOLEWORD | FR_HIDEMATCHCASE | FR_HIDEUPDOWN);
        pDlg->ShowWindow(SW_SHOW);
    }

    LRESULT OnFindReplace(UINT uMsg, WPARAM wParam, LPARAM lParam){
        CFindReplaceDialog* pDlg = CFindReplaceDialog::GetNotifier(lParam);

        // ダイアログが閉じられた場合
        if(pDlg->IsTerminating()){
            m_bOpen = false;
            return 0;
        }

        TCHAR szLine[MAX_LINE];
        m_edit_line.GetWindowText(szLine, sizeof(szLine)/sizeof(TCHAR));
        const CString strLine = szLine;

        const CString strFind = pDlg->GetFindString();
        const CString strReplace = pDlg->GetReplaceString();

        if(pDlg->FindNext()){
            int nStartPos, nEndPos;
            nStartPos = nEndPos = 0;
            m_edit_line.GetSel(nStartPos, nEndPos);

            int nFindPos = strLine.Find(strFind, nEndPos);
            if(nFindPos != -1)
                m_edit_line.SetSel(nFindPos, nFindPos + strFind.GetLength());
        }else if(pDlg->ReplaceCurrent()){
            int nStartPos, nEndPos;
            nStartPos = nEndPos = 0;
            m_edit_line.GetSel(nStartPos, nEndPos);

            int nGap = 0;
            if((nEndPos - nStartPos) > 0){
                m_edit_line.ReplaceSel(strReplace);
                nGap = strReplace.GetLength() - strFind.GetLength();
            }

            int nFindPos = strLine.Find(strFind, nEndPos);
            if(nFindPos != -1){
                m_edit_line.SetSel(nFindPos + nGap,
                    nFindPos + strFind.GetLength() + nGap);
            }
        }else if(pDlg->ReplaceAll()){
            int nFindPos = 0;
            int nFindCount = 0;
            const int nGap = strReplace.GetLength() - strFind.GetLength();
            while((nFindPos = strLine.Find(strFind, nFindPos)) != -1){    
                m_edit_line.SetSel(nFindPos + nGap * nFindCount,
                    nFindPos + strFind.GetLength() + nGap * nFindCount);
                m_edit_line.ReplaceSel(strReplace);
                nFindCount++;
                nFindPos += strFind.GetLength();
            }
        }

        return 0;
    }

    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 "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_FINDDLG
プッシュボタン IDC_BUTTON_REPLACEDLG
エディット IDC_EDIT_LINE

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

 CMainDlgクラスでは、まず、enumによってエディットコントロールの最大文字数をMAX_LINEと定義します。 次にエディットコントロール用にCEditクラスのインスタンスをメンバ変数として宣言します。 これらを使うためには、WM_INITDIALOGメッセージハンドラでコントロールのハンドルを代入する必要があります。 また、検索または置換ダイアログが現在開いているかどうかを表すフラグm_bOpenを宣言します。 これはコンストラクタによってfalseに初期化します。

 次に、WM_INITDIALOGメッセージハンドラで、 LimitText()によってエディットコントロールの最大文字数を設定します。

 次に、2つのボタンコントロール用コマンドメッセージハンドラを追加します。 これらのメッセージハンドラは両方ともOpenFindReplaceDlg()という独自の関数を呼び出します。 OpenFindReplaceDlg()は検索または置換ダイアログを開く関数で引数にBOOL値を取り、 TRUEならば検索ダイアログを、FALSEならば置換ダイアログを開きます。 なお、すでにどちらかのダイアログが開いている場合は何もせずにリターンします。

 検索または置換ダイアログはモードレスダイアログです。 このため、CFindReplaceDialogクラスのオブジェクトはnewによってヒープ上に作成する必要があります。 (そうしないとOpenFindReplaceDlg()関数のスコープを抜けた時にCFindReplaceDialogクラスのオブジェクトも破棄されてしまいます。) 検索または置換ダイアログを作成するためにはCreate()を呼び出します。 第1引数はBOOL値でTRUEならば検索ダイアログを、FALSEならば置換ダイアログを作成します。 第2引数には検索する文字列、第3引数には置換後の文字列、第4引数にはフラグ、 第5引数には親ウィンドウのハンドルを指定でき、第3引数以降は省略可能です。 なお、今回の例では例を単純化するため、フラグに FR_HIDEWHOLEWORDFR_HIDEMATCHCASEFR_HIDEUPDOWNを指定しました。

 次に、メッセージマップに次のようなエントリを追加します。

BEGIN_MSG_MAP_EX(CMainDlg)
    MESSAGE_HANDLER_EX(CFindReplaceDialog::GetFindReplaceMsg(), OnFindReplace)
    ...
    ...
			

CFindReplaceDialog::GetFindReplaceMsg()は検索または置換ダイアログ用のメッセージを登録し、 そのメッセージIDを返します。 メッセージマップによってこのメッセージIDとOnFindReplace()を結びつけることによって、 検索または置換ダイアログからメッセージが発生するとOnFindReplace() が呼び出されるようになります。 次に示すのは、メッセージが発生するタイミングと、メッセージの種類を特定するメンバ関数の対応表です。

タイミング メンバ関数名
[次を検索]ボタンを押した時 FindNext
[置換して次に]ボタンを押した時 ReplaceCurrent
[すべて置換]ボタンを押した時 ReplaceAll
検索または置換ダイアログを閉じた時 IsTerminating

OnFindReplace()内ではこれらのメンバ関数を呼び出すことによって、 どのタイミングでOnFindReplace()が呼び出されたのかを判定し、 それぞれのタイミングに応じた処理を行っています。

 ところで、上の例ではCFindReplaceDialogクラスのオブジェクトはnewによって作成していますが、 deleteによってオブジェクトを削除しているコードが見当たりません。 これは、CFindReplaceDialogクラスのオブジェクトは、 検索または置換ダイアログを閉じたときに自らを削除するからです。

CFindReplaceDialogImplクラスの派生クラスにメッセージマップを用意することによって、 検索置換ダイアログの動作をカスタマイズすることができます。

次に示すのは、WM_INITDIALOGメッセージハンドラと GetFindReplaceMsg()による検索または置換ダイアログ専用メッセージハンドラを用意して、 検索または置換処理をカプセル化する独自の検索置換ダイアログを作成する例です。 この検索置換ダイアログは親ウィンドウの中央に表示されます。 この例ではCustomDlg.hというヘッダファイルを用意し、 そこにCCustomDlgクラスを定義しています。

// CustomDlg.h内
class CCustomDlg : public CFindReplaceDialogImpl<CCustomDlg>
{
    static bool m_bOpenDlg;
    CEdit& m_edit;

public:
    // コンストラクタ
    CCustomDlg(CEdit& edit) : m_edit(edit){
        m_fr.Flags = m_fr.Flags | FR_HIDEWHOLEWORD | FR_HIDEMATCHCASE | FR_HIDEUPDOWN;
    }

    BEGIN_MSG_MAP_EX(CCustomDlg)
        MSG_WM_INITDIALOG(OnInitDialog)
        MESSAGE_HANDLER_EX(GetFindReplaceMsg(), OnFindReplace)
    END_MSG_MAP()

    LRESULT OnInitDialog(HWND hWnd, LPARAM lParam){
        CenterWindow();

        m_bOpenDlg = true;
        m_fr.hwndOwner = m_hWnd;

        return TRUE;
    }

    LRESULT OnFindReplace(UINT uMsg, WPARAM wParam, LPARAM lParam){
        // ダイアログが閉じられた場合
        if(IsTerminating()){
            m_bOpenDlg = false;
            return 0;
        }

        TCHAR* pLine = new TCHAR[m_edit.LineLength() + 1];
        m_edit.GetWindowText(pLine, m_edit.LineLength() + 1);
        const CString strLine = pLine;
        delete [] pLine;

        const CString strFind = GetFindString();
        const CString strReplace = GetReplaceString();

        if(FindNext()){
            int nStartPos, nEndPos;
            nStartPos = nEndPos = 0;
            m_edit.GetSel(nStartPos, nEndPos);

            int nFindPos = strLine.Find(strFind, nEndPos);
            if(nFindPos != -1)
                m_edit.SetSel(nFindPos, nFindPos + strFind.GetLength());
        }else if(ReplaceCurrent()){
            int nStartPos, nEndPos;
            nStartPos = nEndPos = 0;
            m_edit.GetSel(nStartPos, nEndPos);

            int nGap = 0;
            if((nEndPos - nStartPos) > 0){
                m_edit.ReplaceSel(strReplace);
                nGap = strReplace.GetLength() - strFind.GetLength();
            }

            int nFindPos = strLine.Find(strFind, nEndPos);
            if(nFindPos != -1)
                m_edit.SetSel(nFindPos + nGap, nFindPos + strFind.GetLength() + nGap);
        }else if(ReplaceAll()){
            int nFindPos = 0;
            int nFindCount = 0;
            const int nGap = strReplace.GetLength() - strFind.GetLength();
            while((nFindPos = strLine.Find(strFind, nFindPos)) != -1){    
                m_edit.SetSel(nFindPos + nGap * nFindCount,
                    nFindPos + strFind.GetLength() + nGap * nFindCount);
                m_edit.ReplaceSel(strReplace);
                nFindCount++;
                nFindPos += strFind.GetLength();
            }
        }

        return 0;
    }

    static bool IsOpen(){
        return m_bOpenDlg;
    }
};

bool CCustomDlg::m_bOpenDlg = false;
			

CCustomDlgクラスのコンストラクタの引数には CEditオブジェクトの参照を指定します。 CCustomDlgクラスはここで指定したエディットコントロールに対して検索や置換処理を行います。 OnInitDialog()ではm_bOpenDlgという静的メンバ変数にtrueを代入します。 この変数は検索または置換ダイアログが開いているかどうかを示すフラグです。 外部からIsOpen()を呼び出すことによってこのフラグの値が取得できます。 また、OnInitDialog()ではm_fr.hwndOwnerに自分のハンドルを代入しています。 これにより、検索または置換ダイアログ専用のメッセージが自分自身に送られてきます。 メッセージが送られてくるとOnFindReplace()が呼び出されます。 この関数ではコンストラクタ引数で指定されたエディットコントロールに対して、 検索や置換処理を行います。

 次に示すのは、前述のCFindReplaceDialogクラスを使ったプログラムを、 CCustomDlgクラスを使って書き換える例です。

// 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 };

    enum { MAX_LINE = 256};
    CEdit m_edit_line;

    // メッセージマップ
    BEGIN_MSG_MAP_EX(CMainDlg)
        MSG_WM_INITDIALOG(OnInitDialog)
        COMMAND_ID_HANDLER_EX(IDC_BUTTON_FINDDLG, OnButtonFindDlg)
        COMMAND_ID_HANDLER_EX(IDC_BUTTON_REPLACEDLG, OnButtonReplaceDlg)
        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_edit_line = GetDlgItem(IDC_EDIT_LINE);

        m_edit_line.LimitText(MAX_LINE - 1);

        return TRUE;
    }

    void OnButtonFindDlg(UINT uNotifyCode, int nID, HWND hWndCtl){
        OpenFindReplaceDlg(TRUE);
    }

    void OnButtonReplaceDlg(UINT uNotifyCode, int nID, HWND hWndCtl){
        OpenFindReplaceDlg(FALSE);
    }

    void OpenFindReplaceDlg(BOOL bFind){
        if(CCustomDlg::IsOpen())
            return;

        CCustomDlg* pDlg = new CCustomDlg(m_edit_line);
        pDlg->Create(bFind, NULL);
        pDlg->ShowWindow(SW_SHOW);
    }

    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 "CustomDlg.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;
}
			

 OpenFindReplaceDlg()内で独自の検索置換ダイアログを開きます。 まずCCustomDlg::IsOpen()によってすでに検索または置換ダイアログが開いているかどうかチェックし、 開いていなければCCustomDlgクラスのオブジェクトを作成します。

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