ホーム ATL/WTL
ドラッグリストボックス
ドキュメント種別 ATL/WTL に関する文書
最終更新日 2007/03/21
PR
 WTLは、リストボックスコントロールにドラッグ&ドロップによってアイテムの順序を変更する機能を追加した ドラッグリストボックスコントロールを作成するために、 CDragListBoxTおよびCDragListNotifyImplというクラステンプレートを用意しています。 CDragListBoxTCListBoxTから派生しています。 atlctrls.hヘッダでは次のように定義されています。

// atlctrls.h
template <class TBase>
class CDragListBoxT : public CListBoxT< TBase >
{
   ...
   ...
};

typedef CDragListBoxT<ATL::CWindow>   CDragListBox;
			

 WTLのCDragListBoxクラスおよびCDragListNotifyImplクラステンプレートを使うことで、 MFCのCDragListBoxクラスと同等の機能を実現できます。 以下に示すのは、CDragListBoxクラスとCDragListNotifyImplクラステンプレート を使用する例です。


プロジェクトファイル ダウンロード
// stdafx.h
#pragma once

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

#include <atlcrack.h>
#include <atlmisc.h>
#include <atlctrls.h>  // コントロール用クラスを使用するため
			

// MainDlg.h
#pragma once

class CMainDlg : public CDialogImpl<CMainDlg>, public CDragListNotifyImpl<CMainDlg>
{
public:
    enum { IDD = IDD_MAINDLG };

    CDragListBox m_list_drag;

    BEGIN_MSG_MAP(CMainDlg)
        MSG_WM_INITDIALOG(OnInitDialog)
        COMMAND_ID_HANDLER_EX(IDOK, OnOK)
        COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel)
        CHAIN_MSG_MAP(CDragListNotifyImpl<CMainDlg>)  // 基底クラスへチェイン
    END_MSG_MAP()

    BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam){
        // スクリーンの中央に配置
        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_list_drag = GetDlgItem(IDC_LIST_DRAG);
        m_list_drag.MakeDragList();

        m_list_drag.AddString(_T("アイテム1"));
        m_list_drag.AddString(_T("アイテム2"));
        m_list_drag.AddString(_T("アイテム3"));
        m_list_drag.AddString(_T("アイテム4"));
        m_list_drag.AddString(_T("アイテム5"));

        return TRUE;
    }

    BOOL OnBeginDrag(int nCtlID, HWND hWndDragList, POINT ptCursor){
        // カーソル位置のアイテムの左側に挿入マークを描画
        m_list_drag.DrawInsert(m_list_drag.LBItemFromPt(ptCursor));
        return TRUE;
    }

    void OnCancelDrag(int nCtlID, HWND hWndDragList, POINT ptCursor){
        // 挿入マークを消去
        m_list_drag.DrawInsert(-1);
    }

    int OnDragging(int nCtlID, HWND hWndDragList, POINT ptCursor){
        // カーソル位置のアイテムの左側に挿入マークを描画
        m_list_drag.DrawInsert(m_list_drag.LBItemFromPt(ptCursor));
        return 0;
    }

    void OnDropped(int nCtlID, HWND hWndDragList, POINT ptCursor){
        // 挿入マークを消去
        m_list_drag.DrawInsert(-1);

        int nSrcIndex = m_list_drag.GetCurSel();              // 移動元のインデックス
        int nDestIndex = m_list_drag.LBItemFromPt(ptCursor);  // 移動先のインデックス

        if(nSrcIndex == -1 || nDestIndex == -1 || nDestIndex == nSrcIndex)
            return;

        CString strText;
        m_list_drag.GetText(nSrcIndex, strText);
        m_list_drag.DeleteString(nSrcIndex);

        m_list_drag.InsertString(nDestIndex, strText);
        m_list_drag.SetCurSel(nDestIndex);
    }

    void OnOK(UINT uNotifyCode, int nID, CWindow wndCtl){
        EndDialog(nID);
    }

    void OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl){
        EndDialog(nID);
    }
};
			

// SampleProject.cpp
#include "stdafx.h"
#include "resource.h"
#include "MainDlg.h"

CAppModule _Module;

int WINAPI _tWinMain(HINSTANCE hInstance, 
    HINSTANCE /*hPrevInstance*/, LPTSTR /*lpstrCmdLine*/, int /*nCmdShow*/)
{
    HRESULT hRes = ::CoInitialize(NULL);
    ATLASSERT(SUCCEEDED(hRes));

    ::DefWindowProc(NULL, 0, 0, 0L);

    AtlInitCommonControls(ICC_BAR_CLASSES);

    hRes = _Module.Init(NULL, hInstance);
    ATLASSERT(SUCCEEDED(hRes));

    CMainDlg dlg;
    int nRet = (int) dlg.DoModal();

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

    return nRet;
}
			

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

コントロール名 ID
リストボックス IDC_LIST_DRAG

 次に、stdafx.hヘッダでは、CDragListBoxクラスおよびCDragListNotifyImplクラステンプレートを使用するためにatlctrls.hヘッダをインクルードします。

 CDragListNotifyImplクラステンプレートは、メインウィンドウであるCMainDlgクラスの基底クラスとして使用します。 CMainDlgクラスでは、まず、ドラッグリストボックス用にCDragListBoxクラスのインスタンスをメンバ変数として宣言します。 これを使うためには、WM_INITDIALOGメッセージハンドラでコントロールのハンドルを代入する必要があります。

 次に、WM_INITDIALOGメッセージハンドラでは、 CDragListBoxクラスのメンバ関数であるMakeDragList()を呼び出してリストボックスコントロールをドラッグリストボックスコントロール化します。 ドラッグリストボックスコントロールにはあらかじめアイテムを5個追加します。

 次に、メッセージマップに基底クラスであるCDragListNotifyImplへのチェインを追加します。 これにより、ドラッグリストボックスコントロール用のメッセージが登録され、 そのメッセージがメインウィンドウに送られてくるたびに、 チェインによってCDragListNotifyImplのメンバ関数が呼び出されます。 CDragListNotifyImplはメッセージの通知コード別に次のようなメンバ関数を用意しています。

通知コード メンバ関数名 呼び出されるタイミング
DL_BEGINDRAG OnBeginDrag アイテムをドラッグする直前
DL_CANCELDRAG OnCancelDrag アイテムのドラッグを中止した時
DL_DRAGGING OnDragging アイテムをドラッグしている時
DL_DROPPED OnDropped アイテムをドロップした時

CDragListNotifyImplの派生クラスはこれらのメンバ関数をオーバーライドすることによって、 ドラッグリストボックスコントロール内のアイテムがドラッグ&ドロップされたときの動作をカスタマイズすることができます。

 今回の例では、4種類のメンバ関数すべてをオーバーライドします。 まず、OnBeginDrag()およびOnDragging()では、 DrawInsert()を呼び出して挿入マークを描画します。 OnCancelDrag()ではDrawInsert()の引数に-1を指定して挿入マークを消去します。 OnDropped()では挿入マークを消去し、 選択されているアイテムのインデックスとドロップされた位置のアイテムのインデックスを取得します。 これらのインデックスがエラーまたは同じ場合の時は何もしません。 それ以外の場合は、選択されているアイテム文字列を取得してアイテムを削除し、 ドロップされた位置にそのアイテムを挿入します。