邏輯座標和裝置座標的區別
在初始化view的時候,函式會呼叫CXXView類的OnInitialUpdate()函式(XX表示你的工程名)!函式如下:
void CMyScrollView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); CSize sizeTotal; // TODO: calculate the total size of this view //sizeTotal.cx = sizeTotal.cy = 100; SetScrollSizes(MM_TEXT, CSize(500, 400)); }
這裡面可以設定view的大小!比如我這裡設定的是500*400大小。
每次視窗最大化,最小化什麼的,都會發出WM_PAINT訊息,這個訊息由OnPaint函式處理!假如你的CXXView類裡沒有寫OnPaint函式的話,程式會呼叫父類的OnPaint函式,即CScrollView::OnPaint.來看看這個函式裡寫了一些什麼!當然,然後我發現,CScrollView類裡也沒有寫OnPaint函式,自然就呼叫他的父類的OnPaint函式,即CView::OnPaint()。void CView::OnPaint() { // standard paint routine CPaintDC dc(this); OnPrepareDC(&dc); OnDraw(&dc); }
我們再看一看OnPrepareDC這個函式幹了一些什麼!當然由於CScrollView改寫了OnPrareDC這個函式,所以執行CScrollView::OnPrepareDC().
void CScrollView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { if (m_nMapMode == MM_NONE) { TRACE0("Error: must call SetScrollSizes() or SetScaleToFitSize()"); TRACE0("\tbefore painting scroll view.\n"); ASSERT(FALSE); return; } switch (m_nMapMode) { case MM_SCALETOFIT: pDC->SetMapMode(MM_ANISOTROPIC); pDC->SetWindowExt(m_totalLog); // window is in logical coordinates pDC->SetViewportExt(m_totalDev); break; default: ASSERT(m_nMapMode > 0); pDC->SetMapMode(m_nMapMode); break; } CPoint ptVpOrg(0, 0); //設定一個點 if (!pDC->IsPrinting()) //如果沒有列印的話 { ptVpOrg = -GetDeviceScrollPosition();//這個函式用來接收當前視窗的裝置座標 if (m_bCenter) { CRect rect; GetClientRect(&rect); // if client area is larger than total device size, // override scroll positions to place origin such that // output is centered in the window if (m_totalDev.cx < rect.Width()) ptVpOrg.x = (rect.Width() - m_totalDev.cx) / 2; if (m_totalDev.cy < rect.Height()) ptVpOrg.y = (rect.Height() - m_totalDev.cy) / 2; } } pDC->SetViewportOrg(ptVpOrg);//設定視口原點,所謂視口原點,你可以理解為繪畫的原點,當然這個原點是以邏輯座標為準的! CView::OnPrepareDC(pDC, pInfo); }
這個函式執行完了以後,會執行CXXView的OnDraw函式,重繪視窗!
關於什麼是邏輯座標,以及什麼是裝置座標,請看下面兩張圖:
再看一張圖:
需要注意的是:我們點選視窗時返回的是裝置座標,看一個小小的示例:
我寫了一個滑鼠左鍵點選後的訊息處理函式:
void CMyScrollView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CString pos;
pos.Format("Position(%d, %d)", point.x, point.y);
MessageBox(pos);
//CPoint org = GetDeviceScrollPosition();
CScrollView::OnLButtonDown(nFlags, point);
}
處理很簡單,就是輸出傳入的座標點的值!看一看執行結果:
我沒有移動滾動條:
我移動了滾動條,結果又如下:
這就很明顯了,我們在視窗點選滑鼠左鍵的時候,得到的是裝置座標!
我們現在更近一步地探討,我們先在CXXView類裡面加上兩個CPoint變數,一個叫做m_ptOr記錄起點,一個叫做m_ptDe記錄終點!我們要從起點到終點繪製一條直線!
當然,我這只是簡化版的示例操作!
滑鼠左鍵按下時,執行:
void CMyScrollView::OnLButtonDown(UINT nFlags, CPoint point)
{
/*CString pos;
*pos.Format("Position(%d, %d)", point.x, point.y);
*MessageBox(pos);
*/
//現在來繪製一個點!
CClientDC dc(this);
m_ptOr = point; //現在將這個點的座標記錄下來,待會兒再重繪回來!
dc.SetPixel(point, RGB(255, 0, 0));
CScrollView::OnLButtonDown(nFlags, point);
}
滑鼠左鍵彈起時,我們執行:
void CMyScrollView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_ptDe = point; //先將終點座標記錄下來!
CClientDC dc(this);
//然後繪製一條線!
dc.MoveTo(m_ptOr);
dc.LineTo(m_ptDe);
CScrollView::OnLButtonUp(nFlags, point);
}
然後視窗重繪時,我們仍然畫出這條直線!
void CMyScrollView::OnDraw(CDC* pDC)
{
CScrollDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->MoveTo(m_ptOr);//畫一條線
pDC->LineTo(m_ptDe);
}
看一下執行結果:
先將滾動條拉下來,在下方繪製一條直線!
然後切換一下視窗!結果很有趣!
看見沒有,直線移上去了,我來解釋解釋這種現象!不過在這之前,先吃一個飯,然後再來說明吧!
當我們切換視窗時,程式發出WM_PAINT訊息,這個訊息由CXXView::OnPaint()函式處理!具體看檔案前面的說明!呼叫順序如下:
CView::OnPaint() -> CScrollView::OnPrePareDC() -> CXXView::OnDraw()
在CScrollView::OnPrePareDC()中:
所謂視口原點,也可以說是裝置座標系的原點!
然後執行OnDraw函式:
怎麼處理?
利用函式CDC的函式DPtiLP將裝置座標變換成邏輯座標即可!只需要更改OnLButtonDown和OnLButtonUp兩個函式即可!
void CMyScrollView::OnLButtonDown(UINT nFlags, CPoint point)
{
//現在來繪製一個點!
CClientDC dc(this);
OnPrepareDC(&dc);
dc.DPtoLP(&point);
m_ptOr = point; //現在將這個點的座標記錄下來,待會兒再重繪回來!
//dc.SetPixel(point, RGB(255, 0, 0));
CScrollView::OnLButtonDown(nFlags, point);
}
void CMyScrollView::OnLButtonUp(UINT nFlags, CPoint point)
{
CClientDC dc(this);
OnPrepareDC(&dc);
dc.DPtoLP(&point);
m_ptDe = point; //先將終點座標記錄下來!
//然後繪製一條線!
dc.MoveTo(m_ptOr);
dc.LineTo(m_ptDe);
CScrollView::OnLButtonUp(nFlags, point);
}