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

VC 實作客戶區視窗與列表框項目拖曳操作

     編輯:關於VC++

客戶區視窗的建立與 WM_NCCALCSIZE 處理

在 Windows 程式設計中,所謂「客戶區」是指視窗內部可供應用程式繪圖的區域,不包含標題列、選單、捲軸等非客戶區元素。當使用 CreateWindow 建立視窗時,並不需要預先指定客戶區矩形;系統會在需要得知客戶區大小時,傳送 WM_NCCALCSIZE 訊息。在 MFC 中,對應的處理函式為 OnNcCalcSize:

void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp);

此函式透過 NCCALCSIZE_PARAMS 結構中的矩形陣列來調整客戶區範圍。典型實作模式如下:

void CMainFrame::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
    // 必須呼叫基類預設處理,取得初始客戶區矩形
    CFrameWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
    CRect& rc = (CRect&)lpncsp->rgrc[0];
    // 調整客戶區,例如向內縮減 7 像素
    rc.DeflateRect(7, 7);
}

重寫 WM_NCCALCSIZE 時,務必呼叫基類實作以取得預設客戶區,再行修改。同理,在 OnNcPaint 中也應呼叫基類,確保邊框、捲軸等非客戶區元素正確繪製。若自行實作完整的非客戶區繪製(如自訂工具列),則可不呼叫基類。

關於 bCalcValidRects 參數:文件說明其用於指示調整視窗大小時哪部分客戶區內容為有效,但實務上鮮少使用。測試發現視窗初建立時會以 FALSE 呼叫一次,之後調整大小皆為 TRUE。只要在兩種情況下都正確設定第一個矩形(rgrc[0]),即可正常運作。

列表框之間的拖曳放置實作

MFC 未內建列表框間拖曳功能,雖可使用 COM 的 IDropTarget、IDropSource、IDataObject 介面,但代碼龐大且屬大材小用。針對單一應用程式內的控件拖曳,自行實作更為簡潔。

本文提供 CDragDropMgr 類別,封裝拖曳邏輯,支援來源/目標視窗的動態設定。使用步驟如下:

  1. 在主視窗或對話方塊中建立 CDragDropMgr 實例。
  2. 以表格方式定義參與拖曳的子視窗 ID 及角色(來源/目標):
static DRAGDROPWND MyDragDropWindows[] = {
    { IDC_LIST1, DDW_SOURCE },
    { IDC_LIST2, DDW_SOURCE | DDW_TARGET },
    { IDC_EDIT1, DDW_TARGET },
    { 0, 0 },
};
m_ddm.Install(this, MyDragDropWindows);
  1. 覆寫 PreTranslateMessage 將訊息轉交給管理器:
BOOL CMyDlg::PreTranslateMessage(MSG* pMsg)
{
    return m_ddm.PreTranslateMessage(pMsg) ? TRUE :
           CDialog::PreTranslateMessage(pMsg);
}

管理器會偵測滑鼠動作,並透過四個自訂訊息通知應用程式:WM_DD_DRAGENTER、WM_DD_DRAGOVER、WM_DD_DRAGDROP、WM_DD_DRAGABORT。開發者需處理 WM_DD_DRAGENTER(決定是否允許拖曳並提供資料物件)與 WM_DD_DRAGDROP(執行放置動作)。

資料物件與拖曳影像

資料物件繼承自 CDragDropData,需實作三個虛擬函式:OnGetDragSize(取得拖曳影像大小)、OnDrawData(繪製影像)、OnGetData(取得實際資料)。範例提供 CDragDropText 處理文字拖曳:

void CDragDropText::OnDrawData(CDC& dc, CRect& rc)
{
    dc.DrawText(m_text, &rc, DT_LEFT | DT_END_ELLIPSIS);
}

拖曳過程中,管理器利用 CImageList 的 BeginDrag、DragMove、EndDrag 等函式實現平滑的半透明影像移動,開發者只需專注於資料物件的繪製邏輯。

內部運作機制

CDragDropMgr 採用有限狀態機(NONE、CAPTURED、DRAGGING)追蹤滑鼠狀態。在 PreTranslateMessage 中攔截來源視窗的滑鼠訊息,避免逐一子類化每個控件。拖曳影像建立流程:

  1. 應用程式在 WM_DD_DRAGENTER 中建立資料物件並指派給 DRAGDROPINFO::data。
  2. 管理器呼叫 CDragDropData::CreateDragImage,內部依據 OnGetDragSize 建立影像清單,並以 OnDrawData 繪製內容。
  3. 後續滑鼠移動時呼叫 CImageList::DragMove 更新影像位置。

此架構具有高度通用性,新增資料型別僅需衍生 CDragDropData 並實作繪製與尺寸函式即可。

DDTest 程式執行畫面

圖:DDTest 示範程式執行畫面,展示兩個列表框與編輯框之間的拖曳操作。

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