首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++Builder 實現 OLE 拖放:從瀏覽器/Word 直接拖入 Memo 控件

     編輯:關於C++

需求背景

實現從 WordPad、瀏覽器等外部應用拖選文字,拖放到應用程式 TMemo 控件指定光標位置,自動插入文本。

核心架構:TMyDropTarget 類別

繼承 IDropTarget COM 介面,封裝 OLE 拖放目標邏輯,提供三大事件屬性:

  • OnGetFormat:檢查資料格式、決定拖放效果
  • OnProcessData:處理通用 IDataObject
  • OnProcessText:專門處理純文字(CF_TEXT)

整合步驟

1. 表單頭文件宣告

#include "UnitTMyDropTarget.h"

class TForm1 : public TForm {
private:
    TMyDropTarget* DropMemo1;
    void __fastcall DropMemo1Text(IDropTarget* Sender, AnsiString AText, DWORD KeyState, TPoint pt);
public:
    __fastcall TForm1(TComponent* Owner);
    __fastcall ~TForm1();
};

2. 表單實現:初始化與釋放


__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
    OleInitialize(NULL);                    // 初始化 OLE
    DropMemo1 = new TMyDropTarget(Memo1);   // 綁定目標控件
    DropMemo1->OnProcessText = DropMemo1Text; // 綁定文字處理事件
}

__fastcall TForm1::~TForm1() {
    DropMemo1->Release();  // 釋放 COM 物件(非 delete)
    OleUninitialize();
}

3. 文字插入邏輯:精準定位光標


void __fastcall TForm1::DropMemo1Text(IDropTarget* Sender, AnsiString AText, DWORD KeyState, TPoint pt) {
    // 將螢幕座標轉換為 Memo 字符位置
    POINTS p = {pt.x, pt.y};
    *(long*)&p = SendMessage(Memo1->Handle, EM_CHARFROMPOS, 0, *(long*)&p);
    
    Memo1->SelStart = p.x;   // 設定選取起始位置
    Memo1->SelLength = 0;    // 無選取範圍
    Memo1->SelText = AText;  // 插入文字
}

TMyDropTarget 核心實現要點

COM 介面實現

  • QueryInterface:支援 IUnknown、IDropTarget
  • AddRef/Release:引用計數管理,Release 歸零時 delete this
  • DragEnter/DragOver/DragLeave/Drop:標準拖放流程

資料格式檢查 FDataFormatOK


bool __fastcall TMyDropTarget::FDataFormatOK(DWORD KeyState, POINTL pt, DWORD& dpe) {
    FORMATETC fetc = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    bool bOK = _lpDataObj->QueryGetData(&fetc) == S_OK;
    dpe = bOK ? DROPEFFECT_COPY : DROPEFFECT_NONE;
    
    if (_FGetFormat) { /* 觸發用戶事件 */ }
    return bOK;
}

文字資料提取 FProcessData


void __fastcall TMyDropTarget::FProcessData(DWORD KeyState, POINTL pt) {
    if (_FProcessData) { /* 觸發通用事件 */ }
    else if (_FProcessText) {
        STGMEDIUM smed;
        FORMATETC fetc = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
        if (_lpDataObj->GetData(&fetc, &smed) == S_OK) {
            HGLOBAL hData = GlobalLock(smed.hGlobal);
            if (hData) {
                TPoint point(pt.x, pt.y);
                ::ScreenToClient(_OwnerControl->Handle, &point);
                _FProcessText(this, (char*)hData, KeyState, point);
                GlobalUnlock(hData);
            }
            ReleaseStgMedium(&smed); // 釋放儲存介質
        }
    }
}

關鍵技術細節

  1. 座標轉換:拖放事件座標為螢幕座標,需 ScreenToClient 轉換為控件客戶區座標。
  2. 記憶體管理:GlobalLock/Unlock 存取 HGLOBAL,ReleaseStgMedium 釋放 STGMEDIUM。
  3. OLE 初始化:主線程必須呼叫 OleInitialize/OleUninitialize。
  4. 拖放註冊:建構函數 RegisterDragDrop,解構函數 RevokeDragDrop。

擴展應用

同樣架構可輕鬆擴展支援:

  • 檔案拖放(CF_HDROP)
  • 圖片拖放(CF_BITMAP/CF_DIB)
  • 自定義格式(RegisterClipboardFormat)

結語

封裝 IDropTarget 為 TMyDropTarget 元件,讓 C++Builder 應用輕鬆獲得專業級 OLE 拖放體驗,提升使用者操作直覺性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved