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
RegisterClipboardFormatto avoid conflicts with standard system formats. - Memory Management: Always clean up
COleDataSourceandCMyDropTargetinstances to prevent leaks. - Drag Effect: We use
DROPEFFECT_COPY(appropriate for cart scenarios), but you can switch toDROPEFFECT_MOVEif you want to remove items from sources after dropping.
内容的提问来源于stack exchange,提问作者MPW
相关产品推荐
相关产品推荐

