• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Dict.CN 在線詞典, 英語學習, 在線翻譯

            學海苦作舟,書山勤為徑

            留下點回憶

            常用鏈接

            統計

            積分與排名

            Denoise

            English study

            Web技術

            數據壓縮

            一些連接

            最新評論

            第五部分:Drop源(OLE drag&drop之旅)

            歡迎來到OLE拖放旅程系列的第五部分,我們機會到了OLE拖放實現的最后階段,現在需要做的事情就是實現IDropSourceIDropTarget接口;一般我們完成這些,我們就可以在任何程序中添加拖放操作了。

            本部分的目的在于實現一個用作拖放源的簡單程序,它不能接收任何拖放的數據,但這不要緊,因為我們能使用任何平常支持拖放操作的windows程序(例如:WordPad)來測試,程序就是一個windowsEdit控件,它是子類化的,且支持拖操作。

            這個子類的細節在這里不討論,但源碼可以很清晰的說明這個任務。

            成為一個拖放的源對象

            初始化一個拖放操作很簡單,只要調用DoDragDrop這個API就足夠了。

            WINOLEAPI DoDragDrop (
               IDataObject * pDataObject,   // Pointer to the data object
               IDropSource * pDropSource,   // Pointer to the source
               DWORD          dwOKEffect,    // Effects allowed by the source
               DWORD        * pdwEffect      // Pointer to effects on the source
               );

            一旦你調用這個AIPOLE運行時就代表你的程序來接管并處理所有必要的鼠標和鍵盤windows消息,因此你基本上將控制權在調用這個函數的時候交給了OLE

            前兩個參數是COM接口,一個是IDataObject-我們前面已經介紹了這個接口。

            第三個參數是一個DWORD值,它表示源允許的拖動效果,其以位掩碼的方式給出。這些效果是DROPEFFECT_XXX值,通常是DROPEFFECT_MOVEDROPEFFECT_COPY的聯合。如果你想僅僅允許從我們的源復制數據,那么我們應該就指定DROPEFFECT_COPY

            最后一個參數是指向DWORD的指針。該值在DoDragDrop返回的時候可以訪問,包含OLE期望源對象執行的效果和動作,例如:用戶選擇移動還是復制數據?

            執行拖放操作的代碼實際上分成三步:首先我們需要寫一個小的功能函數叫做StringToHandle,它轉換一個char*字符為HGLOBAL,從而我們可以在OLE中使用:

            HANDLE StringToHandle (char *szText, int nTextLen)
            {
                void  *ptr;
                // if text length is -1 then treat as a nul-terminated string
                if(nTextLen == -1)
                    nTextLen = lstrlen (szText) + 1;
                // allocate and lock a global memory buffer. Make it fixed
                // data so we don't have to use GlobalLock
                ptr = (void *)GlobalAlloc(GMEM_FIXED, nTextLen);
                // copy the string into the buffer
                memcpy (ptr, szText, nTextLen);
                return ptr;
            }

            StringToHandle完全不執行錯誤簡單,因此這是你的責任;下一步是準備拖放操作使用的數據:

            FORMATETC fmtetc = 
            { 
                CF_TEXT,            // we will be dropping some text
                0, 
                DVASPECT_CONTENT, 
                -1, 
                TYMED_HGLOBAL       // stored as a HGLOBAL
            };
            STGMEDIUM stgmed = 
            { 
                TYMED_HGLOBAL, 
                {0}, 
                0 
            };
            // Create a HGLOBAL inside the storage medium
            stgmed.hGlobal = StringToHandle ("Hello, World", -1);

            緊接著就是創建兩個拖放操作的COM接口:IDropSourceIDataObject。我們在前面的旅程中實現了CreateDataObject,馬上會實現CreateDropSource

            IDropSource *pDropSource;
            IDataObject *pDataObject;
             
            CreateDropSource (&pDropSource);
            CreateDataObject (&pDataObject, &fmtetc, &stgmed, 1);

            在創建好IDataObjectIDropSource之后就可以調用DoDragDrop了:

            DWORD dwResult;
            // do the drag-drop!
            dwResult = DoDragDrop(pDataObject, pDropSource, DROPEFFECT_COPY, &dwEffect);
            // finished. Check the return values to see if we need to do anything else
            if(dwResult == DRAGDROP_S_DROP)
            {
                if(dwEffect == DROPEFFECT_MOVE)
                {
                    // remove the data we just dropped from active document
                }
            }

            最后一件事情就是清除所有我們使用過的資源,首先刪除我們使用的兩個COM接口,然后刪除包含我們文本的HGLOBAL內存緩沖區。

            // release the COM interfaces
            pDropSource->Release();
            pDataObject->Release();
            ReleaseStgMedium(&stgmed);

            什么時候調用DoDragDrop方法來

            知道了怎么樣初始化拖放操作非常好,但真正重要的是理解將上面的代碼放到你程序的什么位置?

            因為拖放操作是基于鼠標的,它通常在處理windows鼠標消息的時候被初始化,如果你在支持拖放操作的程序中測試(例如WordPad),你會觀察到RichEdit控件有下面的行為:

            1.         當鼠標移動過選中的文本,它的光標形狀變成箭頭

            2.         當按下左鍵的時候,被選擇的不會被刪除,且設置內部狀態來指示可能要開始拖放操作

            3.         當鼠標被第一次移動(并且內部狀態指示左鍵一直按在被選中的文本區域中),拖動操作開始。

            4.         在這個時候,OLE接管并處理所有鼠標消息,直到操作完成

            5.         然而,如果左鍵被釋放了,或者鼠標根本沒有移動,RichEdit選擇部分被清除。

            這些行為在CC++中實現非常簡單,對于我們的子類化的控件,可能是這樣:

             

             

             

             

            case WM_LBUTTONDOWN:
                if(MouseInSelection(hwndEdit))
                {
                    fMouseDown = TRUE;
                    return 0;
                }
                break;
             
            case WM_MOUSEMOUVE:
                if(fMouseDown == TRUE)
                {
                    DoDragDrop (...);
                }
                fMouseDown = FALSE;
                break;
             
            case WM_LBUTTONUP:
                fMouseDown = FALSE;
                break;

            IDropSource接口

            IDropSource是拖放操作中最簡單的接口,除了IUnknown函數外,它僅僅包含兩個需要實現的函數:

            IDropSource 方法

            描述

            QueryContinueDrag

            決定是否應該繼續、取消或完成一個拖放操作,基于鼠標和<Escape> <Control> <Shift> 鍵的狀態。

            GiveFeedback

            為拖放操作的源提供一個用來給出可視化效果的方法,基于鼠標鍵、escapecontrol等鍵的狀態。

            兩個函數由COM/OLE運行時在拖放操作的修飾鍵狀態改變的時候調用,需要做的工作就是實現這個接口-實際上,在準備拖放的時候已經做了很多編碼了。

            實現IDropSource

            同樣我們用一個源文件來實現drop源。在dropsource.cpp中是類的定義,下面就是這些代碼:

             

             

            class CDropSource : public IDropSource
            {
            public:
                //
                // IUnknown members
                //
                HRESULT __stdcall QueryInterface    (REFIID iid, void ** ppvObject);
                ULONG   __stdcall AddRef            (void);
                ULONG   __stdcall Release           (void);
                //
                // IDropSource members
                //
                HRESULT __stdcall QueryContinueDrag (BOOL fEscapePressed, DWORD grfKeyState);
                HRESULT __stdcall GiveFeedback      (DWORD dwEffect);
                //
                // Constructor / Destructor
                //
                CDropSource();
                ~CDropSource();
            private:
                //
                // private members and functions
                //
                LONG    m_lRefCount;
            };

            該類的構造函數除了執行初始化對象引用記數的操作外不執行任何其他的工作。

            IDropSource::QueryContinueDrag

            下面是QueryContinueDrag函數的定義:

            HRESULT QueryContinueDrag(
               BOOL   fEscapePressed,        // Is the <Escape> key being pressed?
               DWORD  grfKeyState,           // Current state of keyboard modifier keys
               );

            函數返回三個值的一個:

            1.       S_OK,拖動操作應該繼續;如果沒有檢查到錯誤就是這個結果,開始拖放操作的鼠標沒有被釋放,也沒有ESC

            2.       DRAGDROP_S_DROP,放操作發生來完成拖操作;如果grfKeyState指出啟動拖放操作的鍵松開了就會發生這個結果。

            3.       DRAGDROP_S_CANCEL;沒有放操作發生時拖操作被放棄,這是fEscapePressed是真的表示ESC鍵被按下的結果。

            作為COM的習慣,下面的兩個行為在拖放操作中應該遵守:

            1.  按下Escape鍵時取消拖放操作

            2.  松開鼠標左鍵的時候,應該執行放操作

            為了符合這些規則,QueryContinueDrag如下實現:

            HRESULT __stdcall CDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
            {
                // if the Escape key has been pressed since the last call, cancel the drop
                if(fEscapePressed == TRUE)
                    return DRAGDROP_S_CANCEL;       
                // if the LeftMouse button has been released, then do the drop!
                if((grfKeyState & MK_LBUTTON) == 0)
                    return DRAGDROP_S_DROP;
                // continue with the drag-drop
                return S_OK;
            }

            IDropSource::GiveFeedback

            這個函數通常在每個程序中都不同,因為沒有一個程序是相同的。然而除非我們提供一個圖形反饋效果,否則我們的是很簡單:

            HRESULT __stdcall CDropSource::GiveFeedback (DWORD dwEffect)
            {
                return DRAGDROP_S_USEDEFAULTCURSORS;
            }

            參數dwEffect(它告訴我們哪個鼠標鍵被按下,哪個鍵盤修飾在使用)在許多拖放操作程序中都可以忽略,簡單的DRAGDROP_S_USEDEFAULTCURSORS來告訴COM在發生任何修飾符改變的時候更新鼠標光標。

            當然,我們可以檢查dwEffectDROPEFFECT_XXX標志來PAINT我們的源窗口并返回S_OK,但為什么要這么麻煩來?

            posted on 2006-03-06 08:49 笨笨 閱讀(4751) 評論(3)  編輯 收藏 引用 所屬分類: OLE Drag&Drop

            評論

            # re: 第五部分:Drop源(OLE drag&drop之旅) 2006-08-17 16:34 來自CSDN的thesuper

            看來看去,沒看到!!  回復  更多評論   

            # re: 第五部分:Drop源(OLE drag&drop之旅) 2006-12-11 15:34 waiting4you(毛毛)

            簡單易懂,謝謝樓主無私奉獻和辛苦翻譯  回復  更多評論   

            # re: 第五部分:Drop源(OLE drag&drop之旅) [未登錄] 2012-02-14 12:21 chunyou128

            lz厲害!膜拜  回復  更多評論   

            国产精品久久久久久福利69堂| 成人国内精品久久久久影院| 欧美777精品久久久久网| 狠狠精品干练久久久无码中文字幕 | 久久亚洲精品国产精品婷婷| 亚洲精品高清国产一线久久| 国内精品久久久久久久久电影网| 久久精品成人欧美大片| 日本精品久久久中文字幕| 久久久久亚洲精品男人的天堂| 中文字幕一区二区三区久久网站 | 一级做a爰片久久毛片人呢| 怡红院日本一道日本久久| 久久久无码精品亚洲日韩蜜臀浪潮 | 亚洲综合伊人久久大杳蕉| 日韩久久无码免费毛片软件| 国产精品女同一区二区久久| 国产精品va久久久久久久| 久久午夜羞羞影院免费观看| 久久狠狠高潮亚洲精品| 天天躁日日躁狠狠久久| 久久久国产精华液| 精品综合久久久久久97超人| 伊人色综合久久天天| 97久久精品无码一区二区| 亚洲中文字幕久久精品无码喷水 | 一级女性全黄久久生活片免费| 综合久久一区二区三区 | 久久青青草原亚洲av无码app| 伊人精品久久久久7777| 欧美伊人久久大香线蕉综合69 | 亚洲精品乱码久久久久久中文字幕| 久久亚洲电影| 区亚洲欧美一级久久精品亚洲精品成人网久久久久 | 午夜精品久久久久久久无码| 91精品国产高清久久久久久国产嫩草 | 久久精品中文字幕第23页| 国产农村妇女毛片精品久久 | 97精品伊人久久久大香线蕉 | 久久国产精品偷99| 蜜桃麻豆www久久国产精品|