本文配套源碼下載
CRectTracker(俗稱“橡皮筋”類)是一個非常有意思的類。你在Windows中經常看到這樣的情況:它可以用做顯示邊界,你也可以扽它的八個角用來放大縮小,或做框選使用。如何通過編程來實現這種功能呢?這就是CRectTracker類的作用;
(框選) (顯示邊界並可以縮放)
你打開上面的那個工程文件,編譯運行一下。你將看到CRectTracker的幾種功能;
下面讓我們來從頭做一個新的工程文件,來慢慢掌握它的功能吧。
建立一個單文檔的工程文件,將其命名為Rect。單擊finish完成工程的建立;先編譯一下,來第一次生成obj文件吧,在它生成的過程中,我們繼續往下講解;
第一步:
在CRectDoc類中生成一個公有的數據成員:m_rectTracker;之所以設成公有,因為要在View中調用它。接著我們來初始化它,在CRectDoc::CrectDoc構造函數中:
CRectDoc::CRectDoc()
{ // TODO: add one-time construction code here
m_rectTracker.m_rect.SetRect(0,0,100,100);
m_rectTracker.m_nStyle=CRectTracker::resizeInside|CRectTracker::dottedLine;
}
其中: m_rect是CRectTracker中用來控制四邊形的大小位置的數據成員, SetRect使用的是View的坐標; m_nStyle是CRectTracker的類型,其中:CRectTracker::resizeInside和CRectTracker::resizeOutside是說明在m_rect的內部還是外部畫區域(它們是互異的),CrectTrakcer::dottedLine是用點劃線來畫四邊形的區域邊界。 其他的值還有: CRectTracker::solidLine:用來畫實線邊界;(和dottedLine是互異的) CRectTracker::hatchedBorder:邊界帶拋面線; CRectTracker::hatchInside:內部帶拋面線; 你可以運行前面的例子,上述參數都有使用。你也可以在第二步中逐一使用它們來加深理解它們各自的含義;
第二步:
接著我門在視圖中畫一個藍色的橢圓; 在CRectView的OnDraw中繼續我們的工作:
void CRectView::OnDraw(CDC* pDC)
{
CRectDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CBrush brush(RGB(0,0,255));//生成藍色的畫刷;
CBrush *oldBrush=pDC->SelectObject(&brush);//將畫刷選進dc;
CRect rect;
//GetTrueRect(&rect)得到CRectTracker中的m_rect的大小,將其傳遞給rect;
pDoc->m_rectTracker.GetTrueRect(&rect);
pDC->Ellipse (rect);//畫橢圓;
//Draw tracking rectangle.
pDoc->m_rectTracker.Draw(pDC);//這句畫才真正的將這個四邊形畫出來;
//Select blue brush out of device context.
pDC->SelectObject (oldBrush);//恢復原來的畫刷;
}
注釋已經在程序裡了,不用再多說,編譯一下。一個橢圓外帶四邊形邊界(點劃線),且四邊形的四周有八個黑點;這就是CRectTracker.,你現在可以改變一下m_nStyle試試看各參數的含義;
第三步:如何象例子中的那樣隨著鼠標的移動自動在橢圓的周圍改變光標呢?很簡單只要將下面的代碼加入到CRectView::OnSetCursor()就可以了:它調用了CRectTracker中的SetCursor()函數:
BOOL CRectView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{ // TODO: Add your message handler code here and/or call default
CRectDoc* pDoc = GetDocument();
if (pWnd == this && pDoc->m_rectTracker.SetCursor(this, nHitTest))
return TRUE;
return CView::OnSetCursor(pWnd, nHitTest, message);
}
編譯運行一下,鼠標變化了。 第四步:我們再做另一個用於鼠標的CRectTracker類。它的作用是在鼠標按下以後可以顯示虛線的選擇框: 先讓我們看看效果:
在CRectView中的加入如下代碼:
void CRectView::OnLButtonDown(UINT nFlags, CPoint point)
{
CRectTracker temp;
temp.TrackRubberBand(this,point,TRUE);
temp.m_rect.NormalizeRect();//正規化;
CView::OnLButtonDown(nFlags, point);
}
編譯運行,當你按下鼠標並拖動,你將看到效果了。
我們如何讓鼠標畫一個“橡皮筋”區域呢? 在CRectTracker類中的成員函數就是:TrackRubberBand(this,point,TRUE); 注意其中的三個參數:
第一個參數,畫“橡皮筋”的窗體的指針,當然是this
第二個參數,畫“橡皮筋”的起始點。
讓我們注意第三個參數,它非常有意思。當你使用FALSE時(TRUE 值是缺省的),你的“橡皮筋”只能從左上到右下的畫,不允許反向。編譯運行一下FALSE這個值。
特別值得注意的是:在TrackRubberBand的過程中是以右鍵的抬起為結束的,這其間並沒有CView的MouseMove發生。這一點一定要記住!這時鼠標畫過的區域已經記錄在temp的m_rect 中了,你可以根據它進行後續的判斷工作。至於下面的正規化語句函數的作用與CRect中的正規化函數的作用一致:使四邊形的四個角的坐標符合右大於左,底大於頂的坐標值。它主要是為了防止你使用TrackRubberBand 的FALSE參數而引起的可能出現的錯誤。
第五步:
讓我們回到那個藍色的橢圓,在開始新的步驟之前,首先來介紹一下HitTest(CPoint point)的功能:當你鼠標被按下的時候,你可以調用這個函數,它將返回鼠標點在了四邊形的什麼位置:
返回值 代表的含義 -1 點在了四邊形的外部 0 左上角 1 右上角 2 右下角 3 左下角(0,1,2,3順時針轉了一圈) 4 頂部 5 右部 6 底部 7 左部(還是順時針轉了一圈) 8 點在了四邊形的內部,但沒有擊中前面的那八個點
可以看出,返回值如果大於等於零則在四邊形區域之內。如果小於則說明不在區域范圍之內。因此我們需要加一個公有的成員函數:BOOL bDraw;為了方便起見,我把它加到CRectView中,(你也許會說,為什麼不加到doc中,我也知道這有勃編程的原理,反正我高興就得,都說C++給人了很大的自由度,所以你也別限制我)。先把它初始化為FALSE,表示不畫邊界,當TRUE時,表示要畫邊界。 定義: class CRectView : public CView
{
…………
public:
BOOL bDraw;
…….
}
初始化:
CRectView::CRectView()
{
// TODO: add construction code here
bDraw=FALSE;
}
將OnDraw改一下,加一句話:
void CRectView::OnDraw(CDC* pDC)
{
CRectDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CBrush brush(RGB(0,0,255));//生成藍色的畫刷;
CBrush *oldBrush=pDC->SelectObject(&brush);//將畫刷選進dc;
CRect rect;
pDoc->m_rectTracker.GetTrueRect (&rect);
//GetTrueRect(&rect)可以得到CRectTracker中的m_rect的大小,將其傳遞給rect;
if(bDraw) //*************新加的語句***************
pDC->Ellipse (rect);//畫橢圓;
//Draw tracking rectangle.
pDoc->m_rectTracker.Draw (pDC);//***這句畫才真正的將這個四邊形畫出來;***
//Select blue brush out of device context.
pDC->SelectObject (oldBrush);//恢復原來的畫刷;
}
編譯運行一下,橢圓的邊界沒有了。
好了,預備知識講完了,讓我們來完成這個程序吧:
void CRectView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
int nIn; //定義一個鼠標的點擊值;
nIn=GetDocument()->m_rectTracker.HitTest(point); //看看點到了哪了
if(nIn<0) //不在四邊形區域內;
{
CRectTracker temp;
temp.TrackRubberBand(this,point,TRUE);
temp.m_rect.NormalizeRect();
CRectTracker interRect;
//在建立一個CRectTracker;用於記錄鼠標與橢圓的交集。
if(interRect.m_rect.IntersectRect(temp.m_rect,GetDocument()->m_rectTracker.m_rect))
bDraw=TRUE; //如果有交集,則畫四邊形的邊界,說明選擇了橢圓
else bDraw=FALSE;
Invalidate(); //引起OnDraw函數的發生;
}
else
//在四邊形區域內:
{
CClientDC dc(this);
GetDocument()->m_rectTracker.Draw(&dc);
GetDocument()->m_rectTracker.Track(this,point,TRUE);
// Track()是CRectTracker中最富魅力的函數。它時時的改變調用者的m_rect;
bDraw=TRUE;
Invalidate();
}
CView::OnLButtonDown(nFlags, point);
}
你也許會問,為什麼我沒有編寫MouseMove函數,它就自動的變大小了呢?這就是Track()函數的功勞,從調用它到抬起鼠標鍵為止,它時刻的改變m_rectTracker的四邊形的大小。然後由於我們使用了Invalidate()函數,所以重新畫了這個橢圓,因此它好象被放大縮小了似的。 我的文章寫完了,還有什麼不懂的地方,寫信給我。在關閉這個文件之前,最好你自己再復習一下,並嘗試一下其他的功能。