//基於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給視窗。
五、PeekMessage和GetMessage的區別:
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本來就是控制項在對話方塊中的位置,如果要調整控制項的位置,修改第二和第三個參數即可);