You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MFC对话框中CStatic控件间拖放实现方案问询

MFC Dialog Drag-and-Drop Between CStatic Controls (Source → Target)

Let's walk through a complete implementation for your "products to shopping cart" scenario. We'll create two custom CStatic-derived classes, handle the drag initiation from sources, and accept drops on the target—with all necessary dialog setup.


Step 1: Derive Custom Source Control (CSourceStatic)

This class handles starting the drag operation when the user clicks and drags a source control.

Header (SourceStatic.h)

#pragma once
#include <afxwin.h>

class CSourceStatic : public CStatic
{
    DECLARE_DYNAMIC(CSourceStatic)

public:
    CSourceStatic();
    virtual ~CSourceStatic();

    // Set/get the product ID associated with this source
    void SetProductID(int id) { m_productID = id; }
    int GetProductID() const { return m_productID; }

protected:
    DECLARE_MESSAGE_MAP()
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

private:
    int m_productID; // Stores the unique ID for this "product"
};

Implementation (SourceStatic.cpp)

#include "SourceStatic.h"
#include <afxole.h>

IMPLEMENT_DYNAMIC(CSourceStatic, CStatic)

CSourceStatic::CSourceStatic() : m_productID(0) {}
CSourceStatic::~CSourceStatic() {}

BEGIN_MESSAGE_MAP(CSourceStatic, CStatic)
    ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

void CSourceStatic::OnLButtonDown(UINT nFlags, CPoint point)
{
    CStatic::OnLButtonDown(nFlags, point);

    // Create an OLE data source to hold our product data
    COleDataSource* pDataSource = new COleDataSource();

    // Convert product ID to a string for transfer
    CString strProductID;
    strProductID.Format(_T("%d"), m_productID);

    // Allocate global memory for the data
    HGLOBAL hData = GlobalAlloc(GHND, (strProductID.GetLength() + 1) * sizeof(TCHAR));
    if (hData)
    {
        LPTSTR pData = (LPTSTR)GlobalLock(hData);
        _tcscpy_s(pData, strProductID.GetLength() + 1, strProductID);
        GlobalUnlock(hData);

        // Cache the data using a custom clipboard format (avoids conflicts)
        static UINT cfProductFormat = RegisterClipboardFormat(_T("CartProductID"));
        pDataSource->CacheGlobalData(cfProductFormat, hData);
    }

    // Start the drag-drop operation (modal, returns when drag ends)
    pDataSource->DoDragDrop(DROPEFFECT_COPY, NULL, NULL);

    // Clean up the data source
    delete pDataSource;
}

Step 2: Derive Custom Target Control (CTargetStatic)

This class accepts drops and processes the incoming product data. We'll use a custom COleDropTarget to handle drag events.

First, Create a Custom Drop Target Class

Add this to your target control's header or a separate file:

class CMyDropTarget : public COleDropTarget
{
protected:
    virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
    virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
    virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
};

Target Control Header (TargetStatic.h)

#pragma once
#include <afxwin.h>
#include "MyDropTarget.h" // Include the drop target class header

class CTargetStatic : public CStatic
{
    DECLARE_DYNAMIC(CTargetStatic)

public:
    CTargetStatic();
    virtual ~CTargetStatic();

    // Called when a product is dropped
    void HandleProductDrop(int productID);

protected:
    DECLARE_MESSAGE_MAP()
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

private:
    CMyDropTarget* m_pDropTarget; // Manages drag-drop events
};

Target Control Implementation (TargetStatic.cpp)

#include "TargetStatic.h"
#include <afxole.h>

IMPLEMENT_DYNAMIC(CTargetStatic, CStatic)

CTargetStatic::CTargetStatic() : m_pDropTarget(nullptr) {}
CTargetStatic::~CTargetStatic() { delete m_pDropTarget; }

BEGIN_MESSAGE_MAP(CTargetStatic, CStatic)
    ON_WM_CREATE()
END_MESSAGE_MAP()

int CTargetStatic::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CStatic::OnCreate(lpCreateStruct) == -1)
        return -1;

    // Initialize and register the drop target
    m_pDropTarget = new CMyDropTarget();
    m_pDropTarget->Register(this);

    return 0;
}

void CTargetStatic::HandleProductDrop(int productID)
{
    // Process the drop: update UI, notify parent dialog, etc.
    CString msg;
    msg.Format(_T("Added Product #%d to Cart!"), productID);
    AfxMessageBox(msg);

    // Send a custom message to the parent dialog for further processing
    GetParent()->SendMessage(WM_ADD_TO_CART, productID, 0);
}

// Implement custom drop target methods
DROPEFFECT CMyDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
    // Check if the data is in our custom format
    UINT cfProductFormat = RegisterClipboardFormat(_T("CartProductID"));
    if (pDataObject->IsDataAvailable(cfProductFormat))
        return DROPEFFECT_COPY; // We accept copies

    return DROPEFFECT_NONE; // Reject other data types
}

DROPEFFECT CMyDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
    // Reuse the drag enter logic to maintain consistent drop effect
    return OnDragEnter(pWnd, pDataObject, dwKeyState, point);
}

BOOL CMyDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
    UINT cfProductFormat = RegisterClipboardFormat(_T("CartProductID"));
    if (pDataObject->IsDataAvailable(cfProductFormat))
    {
        HGLOBAL hData = pDataObject->GetGlobalData(cfProductFormat);
        if (hData)
        {
            LPTSTR pData = (LPTSTR)GlobalLock(hData);
            int productID = _ttoi(pData);
            GlobalUnlock(hData);

            // Pass the product ID to the target control
            ((CTargetStatic*)pWnd)->HandleProductDrop(productID);
            return TRUE;
        }
    }
    return FALSE;
}

Step 3: Set Up the Dialog

Dialog Header (DragDropDialog.h)

#pragma once
#include "SourceStatic.h"
#include "TargetStatic.h"

// Custom message to notify dialog of drops
#define WM_ADD_TO_CART (WM_USER + 100)

class CDragDropDialog : public CDialogEx
{
    DECLARE_DYNAMIC(CDragDropDialog)

public:
    CDragDropDialog(CWnd* pParent = nullptr);
    virtual ~CDragDropDialog();

    enum { IDD = IDD_DRAGDROP_DIALOG };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    virtual BOOL OnInitDialog();

    DECLARE_MESSAGE_MAP()
    afx_msg LRESULT OnAddToCart(WPARAM wParam, LPARAM lParam);

private:
    CSourceStatic m_sourceProduct1;
    CSourceStatic m_sourceProduct2;
    CTargetStatic m_targetCart;
};

Dialog Implementation (DragDropDialog.cpp)

#include "DragDropDialog.h"
#include "afxdialogex.h"

IMPLEMENT_DYNAMIC(CDragDropDialog, CDialogEx)

CDragDropDialog::CDragDropDialog(CWnd* pParent) : CDialogEx(IDD_DRAGDROP_DIALOG, pParent) {}
CDragDropDialog::~CDragDropDialog() {}

void CDragDropDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CDragDropDialog, CDialogEx)
    ON_MESSAGE(WM_ADD_TO_CART, OnAddToCart)
END_MESSAGE_MAP()

BOOL CDragDropDialog::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Create source controls (products)
    m_sourceProduct1.Create(_T("Product 1"), WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER, 
        CRect(50, 50, 150, 80), this, IDC_SOURCE1);
    m_sourceProduct1.SetProductID(1);

    m_sourceProduct2.Create(_T("Product 2"), WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER, 
        CRect(50, 100, 150, 130), this, IDC_SOURCE2);
    m_sourceProduct2.SetProductID(2);

    // Create target control (shopping cart)
    m_targetCart.Create(_T("Shopping Cart"), WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER, 
        CRect(250, 50, 350, 130), this, IDC_TARGET);

    return TRUE;
}

LRESULT CDragDropDialog::OnAddToCart(WPARAM wParam, LPARAM lParam)
{
    int productID = (int)wParam;
    // Add your business logic here: update cart list, calculate totals, etc.
    CString msg;
    msg.Format(_T("Dialog: Product #%d added to cart!"), productID);
    AfxMessageBox(msg);
    return 0;
}

Key Notes

  • SS_NOTIFY Style: Critical for source controls—without it, they won't receive mouse click events to start dragging.
  • Custom Clipboard Format: We use RegisterClipboardFormat to avoid conflicts with standard system formats.
  • Memory Management: Always clean up COleDataSource and CMyDropTarget instances to prevent leaks.
  • Drag Effect: We use DROPEFFECT_COPY (appropriate for cart scenarios), but you can switch to DROPEFFECT_MOVE if you want to remove items from sources after dropping.

内容的提问来源于stack exchange,提问作者MPW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:40:52