第5部:IDropTarget実装

7387 ワード

このセクションでは、IDropTargetインタフェースを実装する必要がある独自のDrop Targetを実装する方法について説明します.
1.どうやって「Drop Target」になるか
フォームがドラッグ&ドロップしたデータを受信できるようにするには、ウィンドウをdropターゲットとして登録し、OLE API RegisterDragDropを呼び出してこのことを完了する必要があります.関数のプロトタイプは次のとおりです.
WINOLEAPI RegisterDragDrop(
  HWND hwnd,                    //Handle to a window that can accept drops
  IDropTarget * pDropTarget        //Pointer to object that is to be target of drop
);

この関数の最初のパラメータは、どのフォームがdropターゲットに登録されているかを示すフォームのHWNDです.2番目のパラメータはIDropTargetポインタで、ドラッグアンドドロップ中にこのインタフェースに呼び出される様々な方法です.
この関数機能とは逆のAPI RevokeDragDropは、このフォームdropターゲットをキャンセルすることを示し、そのプロトタイプは以下の通りである.
WINOLEAPI RevokeDragDrop(
  HWND hwnd                    //Handle to a window that can accept drops
);

このメソッドはフォームが破棄される前に呼び出され、登録ウィンドウがdropターゲットになったときにIDropTargetインタフェースのAddRef関数が内部で呼び出されます.逆に、逆登録時にこのIDataObjectインタフェースのRelease関数が呼び出されるはずです.
RegisterDragDropを呼び出す間、OpeInitialize呼び出しを保証し、CoInitializeまたはCoInitializeExでCOMを初期化しないで、OLEを初期化する必要があります.
2.IDropTargetインタフェース実装
IDropTargetインタフェースには、a)DragEnter:ドラッグアンドドロップ操作が受け入れられるかどうか、および受け入れられた後の効果の4つの方法があります.b)DragOver:DoDragDrop関数による目標フィードバックを提供する.c)DragLeave:dropターゲットがその戻り動作を停止させる.d)Drop:ターゲットウィンドウにデータを置く.これらの関数は、COM/OLEが実行中にオブジェクトが登録されたウィンドウにドラッグされたときに呼び出され、最も本質的にはDoDragDrop関数で呼び出されます.それぞれの関数には異なる機能があり、ドラッグ&ドロップ操作を正しく表現するために最も重要なのは、これらの関数を実現することです.
2.2 SdkDropTarget.h
#ifdef __cplusplus
#ifndef _SDKDROPTARGET_H_
#define _SDKDROPTARGET_H_

#include "SdkDataObject.h"
typedef DWORD DROPEFFECT;

class CLASS_DECLSPEC SdkDropTarget : public IDropTarget
{
public:

    SdkDropTarget();
    ~SdkDropTarget();

    BOOL RegisterDropTarget(HWND hWnd);
    void RevokeDropTarget();
    void AllowDragDrop(BOOL bAllowDrop = TRUE);
    DROPEFFECT FilterDropEffect(DWORD grfKeyState, DROPEFFECT dwEffectSrc);

    IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
    IFACEMETHODIMP_(ULONG) AddRef(void);
    IFACEMETHODIMP_(ULONG) Release(void);

    IFACEMETHODIMP DragEnter(IDataObject *pDataObj, 
        DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
    IFACEMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
    IFACEMETHODIMP DragLeave(void);
    IFACEMETHODIMP Drop(IDataObject *pDataObj, 
        DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);

private:

    //!< The flag indicates whether allow drag or not
    BOOL                m_isAllowDrag;     
    //!< The flag indicates the data available or not     
    BOOL                m_isDataAvailable;      
    //!< The target window handle
    HWND                m_hTargetWnd;     
    //!< The reference count      
    volatile LONG       m_lRefCount;            
    //!< The pointer of IDropTargetHelper interface
    IDropTargetHelper  *m_pDropTargetHelper;    
};

上はSdkDropTargetのクラス宣言で、参照カウントの変数を保存する以外に、BOOLタイプの変数m_があります.isDataAvailableは、現在必要なデータが含まれているかどうかを示すために使用され、メソッド検出を毎回呼び出すことはありません.
m_hTargetWndはdropターゲットのフォームを登録するHWNDです.
m_pDropTargetHelperこれはShellのインタフェースで、ドラッグ&ドロップ操作を実現してくれました.ここではそれを使って実現してくれます.もちろん、使わなくてもいいです.本質を理解することが肝心です.
AllowDragDropがドラッグアンドドロップ操作を許可するかどうかを示すなど、共通の方法も提供されています.RevokeDropTargetはフォームを逆登録し、dropターゲットをキャンセルするために使用されます.これらの関数は、最も本質的なものではありません.
AddRef,Release,QueryInterfaceといういくつかの関数については、重点的に説明しません.
3.DragEnter実装
IDropTarget::DragEnterの関数プロトタイプは次のとおりです.
HRESULT DragEnter(
  IDataObject * pDataObject,  //Pointer to the interface of the source data object
  DWORD grfKeyState,          //Current state of keyboard modifier keys
  POINTL pt,                  //Current cursor coordinates
  DWORD * pdwEffect           //Pointer to the effect of the drag-and-drop operation
);

pDataObject:IDataObjectポインタは、DoDragDropパスを呼び出すポインタです.DragEnterでデータオブジェクトに必要なデータが含まれているかどうかを確認します.
grfKeyState:Ctrl、Shift、Alt、マウスボタンなどのキーボード修飾子の状態を保持します.
pt:マウスが私たちのウィンドウの位置を表し、この点を利用してドラッグ&ドロップが私たちが指定したドラッグ&ドロップ領域で発生したと判断することができます.
pdwEffect:現在許可されているソースのdrop効果を示す出力パラメータ.
DragEnterを実装するには、通常、次のようなことをする必要があります.
1、データ・オブジェクトを検査して、データ・オブジェクトに私たちのデータが含まれているかどうかを確認します.
2.grfKeyStateに格納されているキーボードの状態を確認し、どのようなdrop効果が計算され、出力パラメータで戻り、DROPEFFECT_が戻った場合NONEの場合、このデータは受け入れられないことを示しています.
3.最終的なdrop効果をpdwEffectに格納するポインタ.
最も本質的には、この関数の中でデータが受け入れられるかどうかを決定しなければならない.
実装コードは次のとおりです.
STDMETHODIMP SdkDropTarget::DragEnter(IDataObject *pDataObj,
     DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    if ( NULL == pDataObj )
    {
        return E_INVALIDARG;
    }

    POINT ppt = { pt.x, pt.y };
    DROPEFFECT dwEffect = OnDragEnter(m_hTargetWnd, 
        (SdkDataObject*)pDataObj, grfKeyState, ppt);
    *pdwEffect = dwEffect;
    m_isDataAvailable = (DROPEFFECT_NONE == dwEffect) ? FALSE : TRUE;

    if ( NULL != m_pDropTargetHelper )
    {
        m_pDropTargetHelper->DragEnter(m_hTargetWnd, pDataObj, &ppt, *pdwEffect);
    }
    return S_OK;
}

ここでは、派生クラスがdrag enterの通知を得ることができるように、私が定義した虚メソッドOnDragEnterを呼び出します.ヘッダファイルには宣言がありません.それは重要ではありませんから.ここでは、IDataObjectのQueryGetData関数を呼び出して、指定したデータが現在のデータでサポートされているかどうかを確認できます.
4.DragOver実現
この関数は頻繁に呼び出されますが、この関数の実装はできるだけ効率的であることが望ましいです.キーボードの状態が変化したり、マウスがウィンドウの上を移動したりすると、この関数が呼び出され、一般的にこの関数でマウスカーソルの状態を変更します.たとえばctrlを押すと、どのカーソルを表示すべきか、shiftを押すと、どのカーソルを表示すべきかなど、この関数の原型になります.
HRESULT DragOver(
  DWORD grfKeyState, //Current state of keyboard modifier keys
  POINTL pt,         //Current cursor coordinates
  DWORD * pdwEffect  //Pointer to the effect of the drag-and-drop operation
);

その実現も比較的簡単です.
STDMETHODIMP SdkDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    POINT ppt = { pt.x, pt.y };
    DWORD dwEffect = OnDragOver(m_hTargetWnd, grfKeyState, ppt);
    *pdwEffect = (FALSE == m_isDataAvailable) ? DROPEFFECT_NONE : dwEffect;

    if ( NULL != m_pDropTargetHelper )
    {
        m_pDropTargetHelper->DragOver(&ppt, *pdwEffect);
    }
    return S_OK;
}

5.DragLeave実装
この関数は、マウスカーソルがフォーム(dropターゲット)の外に移動したときに呼び出されるか、ESCが押されたときに呼び出されます.この関数は一般的に何もしなくても、いくつかのフラグの整理作業などに使用できます.たとえば、データがターゲットフォームをドラッグすると、フォームが背景色を変更し、Drag Leaveの場合、この関数で元の色を復元できます.
STDMETHODIMP SdkDropTarget::DragLeave(void)
{
    OnDragLeave(m_hTargetWnd);

    m_isDataAvailable = TRUE;
    if ( NULL != m_pDropTargetHelper )
    {
        m_pDropTargetHelper->DragLeave();
    }
    return S_OK;
}

6.Drop実装
この関数は、マウスの左ボタン(一般的には左ボタン)が解放されたときに呼び出されます.この関数では,IDataObjectの中のデータを取り出し,対応する論理操作を実行することができる.
STDMETHODIMP SdkDropTarget::Drop(IDataObject *pDataObj, 
    DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    if ( NULL == pDataObj )
    {
        return E_INVALIDARG;
    }


    POINT ppt = { pt.x, pt.y };
    if ( NULL != m_pDropTargetHelper )
    {
        m_pDropTargetHelper->Drop(pDataObj, &ppt, *pdwEffect);
    }


    BOOL isOK = OnDrop(m_hTargetWnd, 
        (SdkDataObject*)pDataObj, grfKeyState, ppt);
    return (TRUE == isOK) ? S_OK : E_FAIL;
}

このセクションではIDropTargetインタフェースの実装を示し、次のセクションでは、これらのインタフェース間の呼び出し関係を完全に説明する例を示します.