摘自DoubleLi: http://www.cnblogs.com/lidabo/archive/2012/08/24/2654678.html
mfc控件位置調整和坐標確定
http://my.oschina.net/u/131904/blog/39804
在mfc工程中控件或者窗口位置的調整是經常遇到的,特別是基於對話框的工程。位置的調整包括坐標、長度和寬度的變化,一般在窗口類的OnSize函數中實現。控件位置的調整涉及的函數有:GetWindowRect()、ScreenToClient()、GetClientRect()、MoveWindow()或SetWindowPos(),功能意義如下:
例子:
窗口大小用setwindowpos();為1024*768
控件為 500*500(GetDlgItem(IDC_STATIC)->SetWindowPos(0, 0, 0, 500, 500, NULL);)
GetWindowRect():獲得窗口在屏幕上的矩形坐標,調整控件位置時必須首先獲得該屏幕坐標; 帶四周邊框 GetWindowRect(Rect rect);
得到的窗口為: {top=0 bottom=768 left=0 right=1024}
控件為: {top=30 bottom=530 left=8 right=508}
ScreenToClient():轉換屏幕坐標到客戶區坐標,如果為子窗口,轉換后坐標為相對於父窗口的坐標,若為獨立窗口,轉換后客戶區左上坐標為(0,0);調整子窗口時這一步也是必須的;
GetClientRect():獲得窗口客戶區坐標,左上坐標永遠為(0,0); 不帶邊框 GetClientRect(Rect rect);
得到的窗口為: {top=0 bottom=730 left=0 right=1008}
控件為: {top=0 bottom=500 left=0 right=500}
MoveWindow():調整控件到指定位置;
MoveWindow( int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE ); (有文件鏈接)
SetWindowPos():調整控件的位置,該函數使用更靈活,多用於只修改控件位置而大小不變或只修改大小而位置不變的情況: 以客戶區的起始坐標為原點
SetWindowPos(HWND hWndlnsertAfter, int X,int Y,int cX,int cY,UNIT Flags);(有文件鏈接)
控件位置調整涉及的參變量有:主窗口的cx、cy坐標、參考點坐標。相對於調整位置時調用的函數,變量稍許復雜些。cx、cy坐標為主窗口的寬和高,有OnSize的參數給出,為窗口控件調整提供了變化的范圍,所有的控件為了能夠正常顯示都不能超出這個范圍。其實在開發過程中較難和重要的是參考控件的選擇,其位置相對於主窗口來說必須好確定。常用的策略:
1. 選擇主窗口上位置不隨窗口大小變化的控件為參考;
2. 選擇主窗口上控件的寬或者高固定的控件;
3. 選擇與主窗口滿足固定坐標關系的控件 ;
4. 選擇主窗口上控件位置或者高寬容易確定的控件。
以上四種策略可在實際開發中作為參考!不管遇到什么的情況,一定要清楚:選擇一個
|
本來希望的效果是在對話框上設置兩個picture控件,分別顯示兩幅圖像,然后將兩幅圖像中的相似部分利用一條直線連接起來。要實現這樣的效果需要知道相似位置在這兩幅圖中的坐標以及這兩個控件在對話框上的坐標,然后通過加減運算就可以得到圖像上的相似區域在對話框的坐標,直接將這兩個坐標用直線連接就可以了。 為此,如何獲得控件在對話框上的坐標是關鍵問題。編寫了如下的測試小程序,目的是將兩個picture控件中的點用直線連接起來,比較直觀的是picture控件的四個角,所以程序中是將控件的拐角連接起來。首先在對話框上並排放置兩個同樣大小的picture控件,將他們的標識分別設成IDC_LEFT和IDC_RIGHT,然后添加兩個編輯框用於顯示picture控件的大小,給這兩個編輯框添加相應的數據成員m_row和m_colume。添加一個按鈕用於連接picture控件中的點,為這個按鈕添加成員函數OnMatch() 。 void CControlDlg::OnMatch() { // TODO: Add your control notification handler code here CRect rectL,rectR; GetDlgItem(IDC_LEFT)->GetWindowRect(&rectL);//獲取控件相對於屏幕的位置 ScreenToClient(rectL);//轉化為對話框上的相對位置 GetDlgItem(IDC_RIGHT)->GetWindowRect(&rectR);//獲取控件相對於屏幕的位置 ScreenToClient(rectR);//轉化為對話框上的相對位置 m_row=rectL.bottom-rectL.top; m_colume=rectL.right-rectL.left; UpdateData(FALSE); CClientDC dc(this); dc.MoveTo(rectL.left,rectL.top); dc.LineTo(rectR.right,rectR.bottom); dc.MoveTo(rectL.right,rectL.top); dc.LineTo(rectR.left,rectR.bottom); dc.MoveTo(rectL.left+m_colume/2,rectL.top+m_row/2);//連接兩個控件中心點 dc.LineTo(rectR.left+m_colume/2,rectR.top+m_row/2); |
以用GetSystemMetrics函數可以獲取系統分辨率,但這只是其功能之一,GetSystemMetrics函數只有一個參數,稱之為「索引」,這個索引有75個標識符,通過設置不同的標識符就可以獲取系統分辨率、窗體顯示區域的寬度和高度、滾動條的寬度和高度。
為了使使GetSystemMetrics的功能,我們以獲取系統分辨率為例,並將其中的兩個值用TextOut輸出到窗體中。
第一步:用GetSystemMetrics獲取屏幕的寬度和高度
- int x, y;
- x = GetSystemMetrics(SM_CXSCREEN); //屏幕寬度
- y = GetSystemMetrics(SM_CYSCREEN); //屏幕高度
獲取窗體顯示區域大小
已我現在的了解,獲取窗體顯示區域大小有三種方法。
第一種方法:使用GetSystemMetrics函數
- GetSystemMetrics(SM_CXFULLSCREEN); //獲取最大化窗體的顯示區域寬度
- GetSystemMetrics(SM_CYFULLSCREEN); //獲取最大化窗體的顯示區域高度
|
下面是GetSystemMetrics函數參數nIndex的定義: |
SM_ARRANGE 返回是否預備最小化. SM_CLEANBOOT 返回系統啟動方式: 0 正常啟動 1 安全模式啟動 2 網絡安全模式啟動 SM_CMOUSEBUTTONS 返回值為系統支持的鼠標鍵數,返回0,則系統中沒有安裝鼠標。 SM_CXBORDER, SM_CYBORDER 返回以相素值為單位的Windows窗口邊框的寬度和高度,如果Windows的為3D形態,則 等同於SM_CXEDGE參數 SM_CXCURSOR, SM_CYCURSOR 返回以相素值為單位的標准光標的寬度和高度 SM_CXDLGFRAME, SM_CYDLGFRAME 等同與SM_CXFIXEDFRAME and SM_CYFIXEDFRAME SM_CXDOUBLECLK, SM_CYDOUBLECLK 以相素值為單位的雙擊有效的矩形區域 SM_CXEDGE,SM_CYEDGE 以相素值為單位的3D邊框的寬度和高度 SM_CXFIXEDFRAME, SM_CYFIXEDFRAME 圍繞具有標題但無法改變尺寸的窗口(通常是一些對話框)的邊框的厚度 SM_CXFRAME,SM_CYFRAME 等同於SM_CXSIZEFRAME and SM_CYSIZEFRAME SM_CXFULLSCREEN, SM_CYFULLSCREEN 全屏幕窗口的窗口區域的寬度和高度 SM_CXHSCROLL, SM_CYHSCROLL 水平滾動條的高度和水平滾動條上箭頭的寬度 SM_CXHTHUMB 以相素為單位的水平滾動條上的滑動塊寬度 SM_CXICON,SM_CYICON 系統缺省的圖標的高度和寬度(一般為32*32) SM_CXICONSPACING, SM_CYICONSPACING 以大圖標方式查看Item時圖標之間的間距,這個距離總是大於等於 SM_CXICON and SM_CYICON. SM_CXMAXIMIZED, SM_CYMAXIMIZED 處於頂層的最大化窗口的缺省尺寸 SM_CXMAXTRACK, SM_CYMAXTRACK 具有可改變尺寸邊框和標題欄的窗口的缺省最大尺寸,如果窗口大於這個 尺寸,窗口是不可移動的。 SM_CXMENUCHECK, SM_CYMENUCHECK 以相素為單位計算的菜單選中標記位圖的尺寸 SM_CXMENUSIZE, SM_CYMENUSIZE 以相素計算的菜單欄按鈕的尺寸 SM_CXMIN,SM_CYMIN 窗口所能達到的最小尺寸 SM_CXMINIMIZED, SM_CYMINIMIZED 正常的最小化窗口的尺寸 SM_CXMINTRACK, SM_CYMINTRACK 最小跟蹤距離,當使用者拖動窗口移動距離小於這個值,窗口不會移動。 SM_CXSCREEN, SM_CYSCREEN 以相素為單位計算的屏幕尺寸。 SM_CXSIZE,SM_CYSIZE 以相素計算的標題欄按鈕的尺寸 SM_CXSIZEFRAME, SM_CYSIZEFRAME 圍繞可改變大小的窗口的邊框的厚度 SM_CXSMICON, SM_CYSMICON 以相素計算的小圖標的尺寸,小圖標一般出現在窗口標題欄上。 M_CXVSCROLL, SM_CYVSCROLL 以相素計算的垂直滾動條的寬度和垂直滾動條上箭頭的高度 SM_CYCAPTION 以相素計算的普通窗口標題的高度 SM_CYMENU 以相素計算的單個菜單條的高度 SM_CYSMCAPTION 以相素計算的窗口小標題欄的高度 SM_CYVTHUMB 以相素計算的垂直滾動條中滾動塊的高度 SM_DBCSENABLED 如果為TRUE或不為0的值表明系統安裝了雙字節版本的USER.EXE,為FALSE或0則不是。 SM_DEBUG 如果為TRUE或不為0的值表明系統安裝了debug版本的USER.EXE,為FALSE或0則不是。 SM_MENUDROPALIGNMENT 如果為TRUE或不為0的值下拉菜單是右對齊的否則是左對齊的。 SM_MOUSEPRESENT 如果為TRUE或不為0的值則安裝了鼠標,否則沒有安裝。 SM_MOUSEWHEELPRESENT 如果為TRUE或不為0的值則安裝了滾輪鼠標,否則沒有安裝。(Windows NT only) SM_SWAPBUTTON 如果為TRUE或不為0的值則鼠標左右鍵交換,否則沒有。
Invalidate(TRUE)的閃屏問題
(2010-05-03 02:58:24)在使用Invalidate(TRUE)進行窗口重繪時,總是會遇到閃屏的問題。
一開始以為是繪圖速度過慢照成的,但在對繪圖時間做了一個測試之后發現,即使整個繪圖過程只持續了幾個毫秒,還是會看見很明顯的閃爍,所以時間並不是造成閃爍的決定性因素。
那到底是什么原因呢?現在來看看Invalidate(TRUE)都干了些什么。其實,它只是間接向消息隊列添加了WM_ERASEBKGND和WM_PAINT兩個消息。但是,如果使用Invalidate(FALSE)的話,則只有WM_PAINT消息產生,這時是不會有任何閃爍的。
現在看來,閃爍似乎是由WM_ERASEBKGND消息產生的,事實上,的確與它有關。那WM_ERASEBKGND有干了什么呢?WM_ERASEBKGND消息由OnEraseBkgnd()消息處理函數響應,它的作用就是重繪客戶區背景。我們可以通過向工程里添加WM_ERASEBKGND這個消息,然后在重寫的消息處理函數中將返回語句修改為return TRUE來屏蔽這一功能,這樣做的好處是這時不會重繪背景了,壞處是這時背景也不會被擦出來。
好像還沒有說到真實原因,其實真正的原因就隱含在其中。現在來做一個實驗,分別嘗試一下快速的眨眼和慢速的眨眼,你會發現快速眨眼時我們會感覺眼前的黑色一閃而過,而慢速眨眼時,則會覺得整個過程是連續的,沒有什么異樣。其實閃爍也就是這么回事,即多張不連續圖像的快速切換。這里有三個條件,多張和快速和不連續,而且需要同時具備才會發生閃爍。如果只是兩張,只會感覺到突變,還談不上閃爍;如果頻率慢的話,也相當於兩張圖像的情況了;最后如果是連續圖像的話,那就像是看電影,平穩的過渡也不會讓人覺得不適。
知道了這些,接下來就可以做決策了。
解決方案:
使用Invalidate(FALSE),添加WM_ERASEBKGND消息處理函數或者局部刷新三者選其一,都是可以解決問題的。它們的都是通過除去圖像不連續這一因素來達到目的的。
另外,要說的是GDI的BitBlt()函數是及其高效的,一次操作所需要的時間只有幾到十幾個微秒,所以我們可以放心的使用它,而不用擔心任何效率問題。不過相對於BitBlt()來說StretchBlt()就要慢的多,大概是幾十倍的差別。
還有就是一般的繪圖工作都是先繪制在一個緩沖區上,然后再一次拷貝到屏幕上。
有時,當我們需要利用閃爍的效果的話,也是可以通過多張圖像的快速切換來做到,在這里我們也將兩張圖像的重復切換理解為多張圖像。

