天天看點

如何使MFC對話框上的控件可以拖動

有人問我,怎麼才能使對話框上的控件可以通過滑鼠來拖動. 他的方法是:

1) 從标準控件類派生新的類.

2) 處理控件的滑鼠消息來實作拖動效果.

這樣做當然可以實作,但似乎不太符合正常, 且需要對需要可拖動的所有控件進行子類化以便能響

應控件的滑鼠消息. 在這裡, 我寫了一個類,專門用來處理對控件的拖動, 使用該類隻需要少許修

改包含需要拖動的控件的對話框,而不需要對控件再做任何其它處理.

原理是這樣的: 通過預處理發往對話框及其上控件的滑鼠消息, 如果發現單擊落在某個需要拖動的

控件上, 則啟動拖動過程, 跟蹤滑鼠的運動, 并同時對控件做相應的位置調整. 當滑鼠松開後,整

個拖動過程結束.

類 CMoveCtrlHandler 用來控制整個拖動的過程, 源代碼如下:

// MoveCtrlHandler.h: interface for the CMoveCtrlHandler class.

//

//

#if !defined(AFX_MOVECTRLHANDLER_H__D2C119F0_5B9B_4759_A8EB_8B71F45402B6__INCLUDED_)

#define AFX_MOVECTRLHANDLER_H__D2C119F0_5B9B_4759_A8EB_8B71F45402B6__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#include <afxtempl.h>

typedef CArray<HWND, HWND>  WndowArray;

// for support move controls in the window

class CMoveCtrlHandler 

{

public:

 CMoveCtrlHandler();

 virtual ~CMoveCtrlHandler();

 // set the parent window contained the controls.

 void Initialize(HWND  hParent);

 // add a control to moveable controls array.

 void AddCtrl(HWND hCtrl);

 // hook the message send to the parent window or contorls.

 BOOL HookMessage(MSG* pMsg);

private:

 BOOL HookLoop(HWND hCtrl, POINT  pt);

 void DoMoveCtrl(HWND hCtrl, POINT ptOrig, POINT ptNew);

private:

 WndowArray   m_hWnds;  // a array  contained all the moveable controls

 HWND         m_hParent; // parent window of the contorls.

};

#endif //

!defined(AFX_MOVECTRLHANDLER_H__D2C119F0_5B9B_4759_A8EB_8B71F45402B6__INCLUDED_)

// MoveCtrlHandler.cpp: implementation of the CMoveCtrlHandler class.

//

//

#include "stdafx.h"

#include "MoveCtrlHandler.h"

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

// find the index of the element from a array

template<class T>

int FindArray(CArray<T, T>& arr, T t)

{

 for(int i = 0; i < arr.GetSize(); i++)

 {

  if(arr.GetAt(i) == t)

   return i;

 }

 return  -1;

}

// get the mouse point form a message structure.

static POINT  MakeMessagePoint(const MSG*  pMsg)

{

 POINT  pt;

 pt.x = (long)(short)LOWORD(pMsg->lParam);

 pt.y = (long)(short)HIWORD(pMsg->lParam);

 return pt;

}

//

// Construction/Destruction

//

CMoveCtrlHandler::CMoveCtrlHandler()

{

 m_hParent = NULL;

}

CMoveCtrlHandler::~CMoveCtrlHandler()

{

}

void CMoveCtrlHandler::Initialize(HWND   hParent)

{

 ASSERT(hParent != NULL);

 m_hParent = hParent;

}

void CMoveCtrlHandler::AddCtrl(HWND  hCtrl)

{

 ASSERT(hCtrl  != NULL);

 int id = FindArray(m_hWnds, hCtrl);

 if(id >= 0)  return;

 m_hWnds.Add(hCtrl);

}

BOOL CMoveCtrlHandler::HookMessage(MSG* pMsg)

{

 // drag process started when right click a contorl that contained in array.

 if(pMsg->message != WM_RBUTTONDOWN)

  return FALSE;

 int id = FindArray(m_hWnds, pMsg->hwnd);

 if(id < 0) return FALSE;

 // capture the mouse point

 SetCapture(m_hParent);

 POINT  pt = MakeMessagePoint(pMsg);

 // mapping the point to parent window

 MapWindowPoints(m_hWnds[id], m_hParent, &pt, 1);

 // into dragging loop

 if(!HookLoop(m_hWnds[id], pt))

 {

  return FALSE;

 }

 return TRUE;

}

BOOL CMoveCtrlHandler::HookLoop(HWND hCtrl, POINT pt)

{

 POINT  point = pt;

 MSG    msg;

 POINT  ptNew;

 BOOL   bDrag = FALSE;

 while(GetMessage(&msg, NULL, 0, 0))

 {

  switch(msg.message)

  {

  case WM_RBUTTONDOWN:

   break;

  case WM_MOUSEMOVE:

   // dragging the control.

   ptNew = MakeMessagePoint(&msg);

   if(msg.hwnd != m_hParent)

    MapWindowPoints(msg.hwnd, m_hParent, &ptNew, 1);

   if(ptNew.x !=  point.x || ptNew.y != point.y)

   {

    DoMoveCtrl(hCtrl, point,  ptNew);

    point = ptNew;

    bDrag = TRUE;

   }

   break;

  case WM_RBUTTONUP:

   // leave the dragging mode.

   ReleaseCapture();

   // if the control isn't dragged. do default act and return

   return bDrag;

  default:

   // process the other message 

   TranslateMessage(&msg);

   DispatchMessage(&msg);

   break;

  }

 }

 return bDrag;

}

void CMoveCtrlHandler::DoMoveCtrl(HWND hCtrl, POINT ptOrig, POINT ptNew)

{

 RECT  rc;

 // get the rectangle of the control

 GetWindowRect(hCtrl, &rc);

 ScreenToClient(m_hParent, (LPPOINT)&rc.left);

 ScreenToClient(m_hParent, (LPPOINT)&rc.right);

 // calculate the new rectangle of the control.

 OffsetRect(&rc,  ptNew.x - ptOrig.x, ptNew.y - ptOrig.y);

 MoveWindow(hCtrl, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,

TRUE);

}

按如下步驟為你的對話框添加控件可拖動的功能:

1) 在對話框類聲明一個CMoveCtrlHandler 成員對象.

class CMoveCtrlDlg : public CDialog

{

 ...

 // support to move controls in the dialog.

 CMoveCtrlHandler  m_movectrl;

 ...

};

2) 對話框初始化消息内添加初始化拖動控件代碼并将需要拖動的控件添加到控制清單:

BOOL CMoveCtrlDlg::OnInitDialog()

{

 CDialog::OnInitDialog();

 ...

 // Set the parent window of moveable controls.

 m_movectrl.Initialize(*this);

 // make the contorl IDC_STATIC_1 to be moveable,

 //    make sure the static has SS_NOTIFY  style and its ID cannot be

IDC_STATIC.

 m_movectrl.AddCtrl(*GetDlgItem(IDC_STATIC_1));

 // make the OK button to be moveable

 m_movectrl.AddCtrl(*GetDlgItem(IDOK));

 // make the edit box to be moveable

 m_movectrl.AddCtrl(*GetDlgItem(IDC_EDIT1));

 return TRUE;  // return TRUE  unless you set the focus to a control

}

3) 添加虛函數PreTranslateMessage 的超越:

BOOL CMoveCtrlDlg::PreTranslateMessage(MSG* pMsg)

{

 // TODO: Add your specialized code here and/or call the base class

 m_movectrl.HookMessage(pMsg);

 return CDialog::PreTranslateMessage(pMsg);

}