?? chap42.htm
字號:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="GENERATOR" content="Microsoft FrontPage 3.0">
<title>自定標題的繪制</title>
<link rel="stylesheet" href="../../../include/style.css">
</head>
<body>
<font SIZE="2">
<p><small><a href="../../../index.htm">首頁</a> >> <a href="../../program.htm">程序設計</a>
>> <a href="../cbuilder.htm">C++ Builder</a> >> </small>自定標題的繪制<br>
</p>
<p align="left"><!--webbot bot="ImageMap" rectangle=" (40,1) (71, 23) chap43.htm" rectangle=" (4,1) (36, 23) chap41.htm" src="../ch1/NextBack.gif" width="72" height="24" alt="NextBack.gif (743字節)" border="0" startspan --><MAP NAME="FrontPageMap"><AREA SHAPE="RECT" COORDS="40, 1, 71, 23" HREF="chap43.htm"><AREA SHAPE="RECT" COORDS="4, 1, 36, 23" HREF="chap41.htm"></MAP><a href="../../../_vti_bin/shtml.exe/program/C++/ch4/chap42.htm/map"><img ismap usemap="#FrontPageMap" border="0" height="24" alt="NextBack.gif (743字節)" src="../ch1/NextBack.gif" width="72"></a><!--webbot bot="ImageMap" endspan i-checksum="23282" --></p>
</font>
<p align="left"><font SIZE="2">自定標題的繪制</font></p>
<p><font SIZE="2">由於我們要使用自定的標題,所以你必須將程式所使用的
TForm的BorderStyle性質設為 bsNone,如此你的TForm就不會有標題棒了。 </font></p>
<p><font SIZE="2">再來你就必須自行繪制標題棒,我們希望繪制一個位於左於的標題,因此我們必須處理TForm的OnPaint事件,然後在此事件中繪制標題棒。以下即為其事件處理函式:
</font></p>
<p><font SIZE="2">void __fastcall TForm1::FormPaint(TObject *Sender)</font> </p>
<p><font SIZE="2">{</font> </p>
<p><font SIZE="2"> RECT rc;</font> </p>
<p><font SIZE="2"> ::SetRect(&rc,0,0,ClientWidth,ClientHeight);</font>
</p>
<p><font SIZE="2"> DrawButtonFace(Canvas,rc,1);</font> </p>
<p><font SIZE="2"> Canvas->Pen->Color=clGreen;</font> </p>
<p><font SIZE="2"> Canvas->Brush->Color=clGreen;</font> </p>
<p><font SIZE="2"> Canvas->Rectangle(0,0,20,ClientHeight);</font> </p>
<p><font SIZE="2"> :// 以下略去</font> </p>
<p><font SIZE="2">}</font></p>
<p><font SIZE="2">你可以看到,我們畫出一個寬為20,顏色為綠色的標題棒。因此我們處理WM_NCHITTEST訊息的處理函式也必須做相對應的修改:
</font></p>
<p><font SIZE="2">void __fastcall TForm1::OnNcHitTest(TMessage& Msg)</font> </p>
<p><font SIZE="2">{</font> </p>
<p><font SIZE="2"> TPoint pt;</font> </p>
<p><font SIZE="2"> pt.x=LOWORD(Msg.LParam);</font> </p>
<p><font SIZE="2"> pt.y=HIWORD(Msg.LParam);</font> </p>
<p><font SIZE="2"> pt =ScreenToClient(pt);</font> </p>
<p><font SIZE="2"> RECT rc;</font> </p>
<p><font SIZE="2"> </font> <font SIZE="2">::SetRect(&rc,0,0,20,ClientHeight);</font>
</p>
<p><font SIZE="2"> if (PtInRect(&rc,pt))</font> </p>
<p><font SIZE="2"> Msg.Result = HTCAPTION;</font>
</p>
<p><font SIZE="2"> else</font> </p>
<p><font SIZE="2"> DefaultHandler(&Msg);</font>
</p>
<p><font SIZE="2">}</font></p>
<p><font SIZE="2">OnNcHitTest函式首先取得目前滑鼠所在點,注意,WM_NCHITTEST訊息所傳入的點為相對於螢幕的絕對座標,因此在取得該點後必須利用ScreenToClient函數將它轉為TForm的相對座標值,然後再據以判斷是否落於我們所定義的標題棒范圍內,若是則傳回HTCAPTION值,否則就交由內定的處理函式DefaultHandler來處理。如此就完成了一個位於左方的標題棒了。
</font></p>
<p><font SIZE="2" color="#FF0000">旋轉文字的輸出</font></p>
<p><font SIZE="2">仔細觀察圖一,你會發現它所使用的標題字元的方向,已經因應標題棒的轉向而成為90旋轉的文字,這是如何達成的呢?</font></p>
<p><font SIZE="2">其實說穿了沒什麼,只是利用傳統SDK的繪圖方法來畫出來的。因為在C++Builder的TFont物件并沒有定義文字旋轉的屬性,所以我們只好透過傳統的GDI繪圖方法來達成這個目標。
</font></p>
<p><font SIZE="2">char* msg=Caption.c_str();</font> </p>
<p><font SIZE="2">LOGFONT fontRec;</font> </p>
<p><font SIZE="2">memset(&fontRec,0,sizeof(LOGFONT));</font> </p>
<p><font SIZE="2">fontRec.lfHeight = -13;</font> </p>
<p><font SIZE="2">fontRec.lfWeight = FW_NORMAL;</font> </p>
<p><font SIZE="2">fontRec.lfEscapement = 900; // 旋轉文字的關鍵</font> </p>
<p><font SIZE="2">lstrcpy(fontRec.lfFaceName,"細明體");</font> </p>
<p><font SIZE="2">HFONT hFont=CreateFontIndirect(&fontRec);</font> </p>
<p><font SIZE="2">HFONT hOld=::SelectObject(Canvas->Handle,hFont);</font> </p>
<p><font SIZE="2">::SetRect(&rc,0,0,20,ClientHeight);</font> </p>
<p><font SIZE="2">::SetTextColor(Canvas->Handle,RGB(255,255,255));</font> </p>
<p><font SIZE="2">::TextOut(Canvas->Handle,3,ClientHeight-3,msg,lstrlen(msg)); </font></p>
<p><font SIZE="2">::SelectObject(Canvas->Handle,hOld);</font> </p>
<p><font SIZE="2">::DeleteObject(hFont);</font></p>
<p><font SIZE="2">以上的程式我不打算詳加說明,簡單地說,它就是建立一個旋轉90度的字形,然後將字串以此字形畫於螢幕上,此段程式碼的關鍵在於你必須知道Canvas->Handle即是代表GDI繪圖的HDC。其馀的函式說明你都可以在一般講解傳統Windows
SDK繪圖的書籍中找到。</font></p>
<p><font SIZE="2">由此我們也可以得到一個經驗:雖然C++Builder的快速程式發展環境已經取代了傳統SDK式的程式設計中大部份的工作,然而通曉一些必要的SDK程式技巧卻可以使你上一層樓。所以我建議你在『行有馀力』時,不妨可以看看SDK相關書籍,充實基礎知識。或許我們可以名之為『立足
BCB,放眼 SDK』的學習態度吧!</font></p>
<p><font SIZE="2" color="#FF0000">其他說明</font></p>
<p><font SIZE="2">在本程式中因為TForm的BorderStyle性質為bsNone。因此并沒有外框,為了美化視窗,所以我寫了幾個輔助函式來繪出立體框。若你在其他程式中有類似的需求,也可以使用之。
</font></p>
<p><font SIZE="2">void DoRect(TCanvas* Canvas,RECT& rect,COLORREF cTopColor,COLORREF
cBottomColor)</font> </p>
<p><font SIZE="2">{</font> </p>
<p><font SIZE="2"> POINT p[3];</font> </p>
<p><font SIZE="2"> p[0].x = rect.right;</font> </p>
<p><font SIZE="2"> p[0].y = rect.top;</font> </p>
<p><font SIZE="2"> p[1].x = rect.left;</font> </p>
<p><font SIZE="2"> p[1].y = rect.top;</font> </p>
<p><font SIZE="2"> p[2].x = rect.left;</font> </p>
<p><font SIZE="2"> p[2].y = rect.bottom;</font> </p>
<p><font SIZE="2"> Canvas->Pen->Color=TColor(cTopColor);</font> </p>
<p><font SIZE="2"> Canvas->Polyline(p,3);</font> </p>
<p><font SIZE="2"> p[1].x = rect.right;</font> </p>
<p><font SIZE="2"> p[1].y = rect.bottom;</font> </p>
<p><font SIZE="2"> p[2].x--;</font> </p>
<p><font SIZE="2"> Canvas->Pen->Color=TColor(cBottomColor);</font>
</p>
<p><font SIZE="2"> Canvas->Polyline(p,3);</font> </p>
<p><font SIZE="2">}</font></p>
<p><font SIZE="2">void Frame3D(TCanvas* Canvas,RECT& rect,COLORREF cTopColor,COLORREF
cBottomColor,int iColWidth)</font> </p>
<p><font SIZE="2">{</font> </p>
<p><font SIZE="2"> rect.bottom--; rect.right--;</font> </p>
<p><font SIZE="2"> while (iColWidth > 0)</font> </p>
<p><font SIZE="2"> {</font> </p>
<p><font SIZE="2"> iColWidth--;</font> </p>
<p><font SIZE="2">
DoRect(Canvas,rect,cTopColor,cBottomColor);</font>
</p>
<p><font SIZE="2"> InflateRect(&rect,-1,-1);</font>
</p>
<p><font SIZE="2"> }</font> </p>
<p><font SIZE="2"> rect.bottom++; rect.right++;</font> </p>
<p><font SIZE="2">}</font></p>
<p><font SIZE="2">void DrawButtonFace(TCanvas* Canvas,RECT& rect,int nBevelWidth)</font>
</p>
<p><font SIZE="2">{</font> </p>
<p><font SIZE="2"> Canvas->Brush->Color=clBtnFace;</font> </p>
<p><font SIZE="2"> Canvas->FillRect(TRect(rect));</font> </p>
<p><font SIZE="2">
Frame3D(Canvas,rect,::GetSysColor(COLOR_BTNSHADOW),::GetSysColor
</font></p>
<p><font SIZE="2">
(COLOR_WINDOWFRAME),nBevelWidth);</font>
</p>
<p><font SIZE="2">
Frame3D(Canvas,rect,::GetSysColor(COLOR_BTNHIGHLIGHT),::GetSysColor
</font></p>
<p><font SIZE="2">
(COLOR_BTNSHADOW),nBevelWidth);</font>
</p>
<p><font SIZE="2">}</font></p>
<p><font SIZE="2">這叁個函式中最重要的就是 DrawButtonFace,它是用來在一個矩形范圍中畫出一個類似Button的立體方框,在本程式中我用它來畫出TForm的邊框。你可以由圖一看出它的視覺效果。</font>
</p>
<p><font SIZE="2">程式的改進</font></p>
<p><font SIZE="2">前面我們提到改進bsNone視窗視覺效果的方式是利用自行撰寫的DrawButtonFace函式來達成,它雖不失為一個解決問題的方法,但是卻也因此增加了程式的復雜度,再來我為你示范一種利用改寫CreateParams函式的技巧來達成類似功能的方法。
</font></p>
<p><font SIZE="2">CreateParams是一個虛擬函式,你可以經由它來修改windows的style,因為原先在C++Builder中所定義的Form是一個Dialog(交談窗),而交談窗的外形內定是有標題棒的,然而如果我們如前面的方法將外框設為bsNone
的話,那就必須自行畫出假的視窗外框,否則看起來不好看。</font></p>
<p><font SIZE="2">但是在Windows系統中除了前面的Dialog式的視窗之外,還提供了另一種POPUP式的視窗,只不過在C++Builder并未提供該選項罷了。因此我們其實可以透過改寫CreateParams的方式來產生WS_POPUP形式的視窗,如此一來我們就不必煞費周章地撰寫畫外框的函式了。它的程式其實很簡單,只是將Params.Style的WS_DLGFRAME
(代表使用Dialog外框),改成另一種WS_POPUP
(彈出式視窗)。要做到以上效果,只要利用and及or運算就可以達到了。以下即為其程式碼:
</font></p>
<p><font SIZE="2">void __fastcall TForm1::CreateParams(TCreateParams& Params)</font> </p>
<p><font SIZE="2">{</font> </p>
<p><font SIZE="2"> TForm::CreateParams(Params);</font> </p>
<p><font SIZE="2"> Params.Style |= WS_POPUP;</font> </p>
<p><font SIZE="2"> Params.Style ^= WS_DLGFRAME;</font> </p>
<p><font SIZE="2">}</font></p>
<p><font SIZE="2">圖二為改寫後的程式執行結果,不僅程式簡潔了許多,而且外觀也較好看,那是因為我們在畫標題棒時,不會像前面一樣將外框蓋住的緣故。
</font></p>
<p><img SRC="IMG00002.GIF" WIDTH="364" HEIGHT="216"></p>
<p><font SIZE="2">圖二 利用CreateParams技巧的新程式。<br>
</font></p>
<p align="right"><font SIZE="2"><!--webbot bot="ImageMap" rectangle=" (40,1) (71, 23) chap43.htm" rectangle=" (4,1) (36, 23) chap41.htm" src="../ch1/NextBack.gif" width="72" height="24" alt="NextBack.gif (743字節)" border="0" startspan --><MAP NAME="FrontPageMap1"><AREA SHAPE="RECT" COORDS="40, 1, 71, 23" HREF="chap43.htm"><AREA SHAPE="RECT" COORDS="4, 1, 36, 23" HREF="chap41.htm"></MAP><a href="../../../_vti_bin/shtml.exe/program/C++/ch4/chap42.htm/map1"><img ismap usemap="#FrontPageMap1" border="0" height="24" alt="NextBack.gif (743字節)" src="../ch1/NextBack.gif" width="72"></a><!--webbot bot="ImageMap" endspan i-checksum="41510" --></font></p>
<font SIZE="2">
<p><small><a href="../../../index.htm">首頁</a> >> <a href="../../program.htm">程序設計</a>
>> <a href="../cbuilder.htm">C++ Builder</a> >> </small>自定標題的繪制</p>
</font>
</body>
</html>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -