1. 程式人生 > >邏輯座標和裝置座標的區別

邏輯座標和裝置座標的區別

微笑吐舌頭如果我們想製作一個擁有滾動條的應用程式,那就只需要讓你的view類派生自CScrollView類即可,CScrollView類派生自CView類!
微笑吐舌頭在初始化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);
}