SDK:用GetWindowRect GetClientRect 获得控件在客户区的RECT

//基于SDK

//目的:获得控件在客户区的坐标,封装类似MFC中类似void  ScreenToClient( LPRECT lpRect )方法

//返回RECT包括左上角的坐标,不只是(0,0)

 

    虽然MFC封装了不少Windows的API类库,但是有时候使用MFC 时总是不如人意!特别别扭,还是钟情在SDK平台下编写。于是问题出现:

在SDK平台下,能获得窗口的大小的常用API函数有: 

BOOL GetWindowRect(HWND hWnd,LPRECT lpRect);       //获得窗口相对屏幕的大小

BOOL GetClientRect(HWND hWnd, LPRECT lpRect );             //获得窗口相对客户区的大小

BOOL ScreenToClient(HWND hWnd, LPPOINT lpPoint);    //把屏幕坐标转换为客户区坐标

注意ScreenToClient的参数 lpPoint是指向POINT结构指针,明显不能达到要求。

在MFC中CWin类库中提供void ScreenToClient( LPRECTlpRect )方法,而在SDK下没有,造成很不便。

 

提供以下算法能很好解决问题:

BOOL GetControlClientRect(HWND hwnd,int iID,RECT &ControlRect)

{

 RECT WinRect,ClienWinRect; 

// WinRect 是主窗口相对屏幕的大小

// ClienWinRect是主窗口相对客户区的大小

//ControlRect是控件相对客户区的坐标

 GetWindowRect(hwnd,&WinRect);

 GetClientRect(hwnd,&ClienWinRect);

  GetWindowRect(GetDlgItem(hwnd,iID),&ControlRect);

 int SizeFim=(WinRect.right-WinRect.left-ClienWinRect.right)/2;    //获得客户区的边框大小

 ControlRect.left=ControlRect.left-WinRect.left-SizeFim;  

 ControlRect.right=ControlRect.right-WinRect.left-SizeFim;

 ControlRect.top=ControlRect.top-(WinRect.bottom-SizeFim-ClienWinRect.bottom);

 ControlRect.bottom=ControlRect.bottom-(WinRect.bottom-SizeFim-ClienWinRect.bottom);

 return TRUE;

}

//另外一种方法是保证没有错误的。

void NewScreenToClient(HWND hwndPar,HWND hwndChild,RECT &rect)

{

POINT pt;

int ixPos,iyPos;

GetWindowRect(hwndChild,&rect);

pt.x=rect.left;

pt.y=rect.top;

ScreenToClient(hwndPar,&pt);

rect.left=pt.x;

rect.top=pt.y;

pt.x=rect.right;

pt.y=rect.bottom;

ScreenToClient(hwndPar,&pt);

rect.right=pt.x;

rect.bottom=pt.y;

}

//可以设置为全局函数。这样在较快地得到子窗口相对父窗口的客户区坐标。

/////////////////////////////////////////////////////////////

转自http://blog.csdn.net/yupeng1949/article/details/6093989

我发现很多人转载的都没有经过测试!!!

上面这位兄弟的可以用,非常感谢。

----------------------------------------

再转一些MFC的方法

1. 函数原型(源自MSDN)

      virtual BOOL PreTranslateMessage(MSG* pMsg);

       功能:

      重载该函数可以实现窗口消息在派发给窗口函数TranslateMessage()DispatchMessage()之前的过滤.缺省的实现是完成加速键的翻译.因为您必须在你的重载版本中调用CWinApp:PreTranslateMessage()函数.

      MFC中,PreTranslateMessage()是虚函数,我们可以重载它来处理键盘和鼠标消息。

      SDK中,这又有所不同,我们必须在回调函数中
       LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)处理消息,它和PreTranslateMessage起的作用是类似的。只是MFC封装的更好而已。

2. 说明

       该函数表示在消息处理(TranslateMessage()DispatchMessage())前所作的操作,如果函数返回值为TRUE,那么消息处理即终止,不会调用TranslateMessage()DispatchMessage()来翻译和分发消息给相应的窗口;若返回值为FALSE,才会调用翻译和分发消息函数。

       该函数是MFC消息控制流最具特色的地方,它是CWnd类的虚拟函数,通过重载这个函数,我们可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来。

    win32程序中,关于消息有两种传递方式:

      a. MFC消息,MFC会把所有的消息一条条放到一个AFX_MSG_MAP_ENTRY结构中,形成一个数组,该数组存放了所有的消息和与它们相关的参数。也可以说是放到消息队列里去。

      b. 采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息。

    这两种方式中只有第一种(穿过消息队列的消息)才受PreTranslateMessage()影响,第二种消息并不会理睬PreTranslateMessage()的存在。

3. 其他

       PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,绝大多数本窗口的消息都要通过这里,比较常用,当你需要在MFC之前处理某些消息时,常常要在这里添加代码.

       MFC消息控制流最具特色的地方是CWnd类的虚拟函数PreTranslateMessage(),通过重载这个函数,我们可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来。只有穿过消息队列的消息才受PreTranslateMessage()影响,采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息根本不会理睬PreTranslateMessage()的存在。

       一、是否调用TranslateMessage()DispatchMessage()是由一个名称为PreTranslateMessage()函数的返回值决定的,如果该函数返回TRUE,则不会把该消息分发给窗口函数处理。

       二、传给PreTranslateMessage()的消息是未经翻译过的消息,它没有经过TranslateMessage()处理。例如可以在该函数中使用(pMsg->wParam == VK_RETURN)来拦截回车键。

       三、在WindowProc里不能处理WM_CHAR消息。(WindowProc函数见MFC消息响应机制一文)

       四、SetWindowText会发送WM_CHAR给窗口。

       五、PeekMessageGetMessage的区别:

      GetMessage在没有消息的时候等待消息,效率低。PeekMessage没有消息的时候立刻返回,所以CPU占用率高。因为游戏不能靠Windows消息驱动,所以要用PeekMessage();

       在一个WIN32程序中,WINDOWS会将消息传递给相应的窗口。但是消息不是立即就被传递给相应的窗口,而是会从整个程序最顶层的窗口传递到下一级窗口,再传递到下一级窗口,直到传递给目标窗口。在整个过程中,有些消息,在某些特定的情况下,无法默认传递到目标窗口的。比如用户在EDIT控件中按下回车键,CANCEL键等,如果EDIT窗口之前有对话框窗口,对话框会默认处理回车消息(即响应ONOK函数,然后关闭对话框),然后退出消息传递。所以 EDIT会收不到。要解决这个问题,可以在EDIT窗口之前所有的对话框中重载PreTranslateMessage函数,然后在函数内加上:   如此,就可避开对话框默认处理,将消息传递下去。注意:只有对话框才会默认处理按下回车,CANCEL消息,其他控件窗口则不会,所以在其他窗口中不必重载PreTranslateMessage函数,当然如果重载了也不会错。

if (pMsg->message== WM_KEYDOWN&& pMsg->wParam== VK_RETURN)    //如果消息类型为WM_KEYDOWN并且用户按下的是回车
         return FALSE;   
//不翻译消息,直接将消息传递下去。具体可查MSDN。注意,这里返回值不能为TRUE,TRUE的意思是翻译消息后退出消息传递,如此一来虽然也能避开对话框默认处理,但是会退出消息传递,这样EDIT控件照样得不到消息。(我一开始所犯的错误)

附:关于PreTranslateMessage()函数的小程序示例:

BOOL CUserDlg::PreTranslateMessage(MSG* pMsg)
{
    
if(pMsg->message== WM_KEYDOWN)//判断是否有按键按下
    {
       
switch(pMsg->wParam)
       
{
          
case VK_DOWN:     //表示是方向键中的向下的键
              
// code here
               break;
          
case VK_UP:      //表示是方向键中的向上的键
               
// code here
               break;
          
default:
               
break;
        }

    }

}

方法1:

BOOL AcameraCT::PreTranslateMessage(MSG* pMsg)
{
  int buID;
 buID= GetWindowLong(pMsg->hwnd,GWL_ID);//由窗口句柄获得ID号,GetWindowLong为获得窗口的ID号。
  if(pMsg->message==WM_LBUTTONDOWN) 
 {     
  if(buID==IDC_BUTTON_CT1) //按下
  {  
   //在这里添加单击按下事件的程序
  } 
 }
 if(pMsg->message==WM_LBUTTONUP) 
 { 
  if(buID==IDC_BUTTON_CT1)
  {  
   //在这里添加单击松开事件的程序
  } 
 }
 return CDialog::PreTranslateMessage(pMsg);
}

 

方法2:

BOOL AcameraCT::PreTranslateMessage(MSG* pMsg)
{
  int buID;
 CWnd* pWnd=WindowFromPoint(pMsg->pt); //获得指定点句柄
 buID=pWnd->GetDlgCtrlID();//获得该句柄的ID号。
  if(pMsg->message==WM_LBUTTONDOWN) 
 {     
  if(buID==IDC_BUTTON_CT1) //按下
  {  
   //在这里添加单击按下事件的程序
  } 
 }
 if(pMsg->message==WM_LBUTTONUP) 
 { 
  if(buID==IDC_BUTTON_CT1)
  {  
   //在这里添加单击松开事件的程序
  } 
 }
 return CDialog::PreTranslateMessage(pMsg);
}

 

 

在MFC中判断单击的是某个按钮时可以和SDK一样在WM_COMMAND获取LPARAM获得,也可以通过判断WM_LBUTTONDOWN和WM_LBUTTONUP后的坐标参数是否在按钮区域。

在MFC中使用CRect较RECT简单点

CRect rt;
GetDlgItem(IDC_CB)->GetWindowRect(rt);   //取得在屏幕中的位置
ScreenToClient(rt);          //转换到在对话框中的位置
rt.left和rt.top为该控件在对话框中的位置;
GetDlgItem(IDC_CB)->SetWindowPos(NULL,rt.left,rt.top,0,0,SWP_NOSIZE);//设置该位置(其实还是在原位置,因为rt.left和rt.top本来就是控件在对话框中的位置,如果要调整控件的位置,修改第二和第三个参数即可);

原文地址:https://www.cnblogs.com/zhiweiyouzhishenghuo/p/5005527.html