问KXY兄

yanzt 2000-04-03 09:34:00
我下载了你的修改Form属性的控件,并且安装成功,可是我在C++Builder4中使用有问题,我在设计的时候可以更改Form的菜单、按扭,可是运行的时候,Form上却什么都没有,只能用Program Reset强制终止程序执行,不知道为什么?
...全文
348 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
wxz 2000-06-23
  • 打赏
  • 举报
回复
窗口类是否TCoolForm
你仔细看看吧
yanzt 2000-06-23
  • 打赏
  • 举报
回复
我发现Align属性没有alTop呀?怎么回事?
wxz 2000-06-22
  • 打赏
  • 举报
回复
yanzt:
我下载了这个控件,确实很好用,不过看来你用错了,把这个控件放到窗口上后,窗口的标题栏就被屏蔽了,你应该把这个控件的Align改为alTop,然后把它的Height属性改为合适的大小,然后在它的上面放置你喜欢的图片,控制按钮,这个就是你的程序运行时的标题栏.
kxy 2000-06-09
  • 打赏
  • 举报
回复
在本站的下载中心有.
wxz 2000-06-08
  • 打赏
  • 举报
回复
问kxy兄:
能不能把这个控件发给我看看
kxy 2000-06-08
  • 打赏
  • 举报
回复
此台湾人的办法一般(基本技巧而已),我找到的那个控件open source的,使用的方法要好一些.
还可以在form的Title上放置自己的按钮.使用上很方便.
wxz 2000-06-08
  • 打赏
  • 举报
回复
<创世纪的C++ Builder>是一个台湾高人写的文章,用C++ Builder的人实在值得看看,我忘了在那里下载的了,所以上面发了相关的一篇. 解决你的问题的方法是建立一个无标题栏的窗口,然后自画一个标题栏.关键是接管WM_NCHITTEST消息.
wxz 2000-06-08
  • 打赏
  • 举报
回复
以C++ Builder處理Windows 訊息(Message)


前言


雖然C++Builder為一RAD式的程式發展工具,程式設計師在大多數情況下不需理會Windows訊息的細節,只要將心思放在軟體元件的事件處理函式即可。然而由於Windows作業系統終究是一個以訊息驅動的系統,因此架構其上的的應用程式自然無法自外於系統之外,在遭遇到C++Builder沒有定義的事件時,Windows訊息處理能力仍然是C++Builder程式人不可或缺的能力。


不可否認地,C++Builder所提供的事件處理能力已具備了某一程度的完備性,然而我們也必須承認,在C++Buider建構的VCL美麗新世界中,仍然不免有漏網之魚。例如使用者自定訊息的處理,Winsock訊息的處理及一些Windows訊息如WM_NC**** 系列的訊息都是C++Builder的物件模型所未包含的。


在本文中我將告訴你如何以C++Builder來處理Windows訊息,並透過此一能力,來達成在一般VCL元件所無法做到的功能。


何謂Window訊息(Message)


大家都知道 Windows是一套以訊息驅動(Message Driven)的作業系統。然而對於訊息本身卻諱莫如深,只知其然而不知其所以然,雖然C++Builder將某些Windows訊息封裝於事件(Event)系統中,但身為一個Windows程式設計師,實有必要瞭解Windows的訊息系統。


所謂訊息是由Windows作業系統送往程式的事件。它是系統中各個物件溝通的方式,舉例來說,當移動滑鼠、按下滑鼠鍵、改變視窗大小時,Windows都會送出訊息以通知程式。當然,為了要辨別事件的內容,Windows系統中定義了許多的訊息,如WM_PAINT,WM_CHAR等等。


當事件發生時,Windows會判斷該事件必須由那個程式接收,然後將事件以訊息的方式送往程式的視窗中。雖然在Windows系統中包含了數以百計的事件,但是作業系統並沒有為各個事件設計不同的訊息結構,而是以一個一般性的結構來描述訊息,這個結構在C++Builder就稱是TMessage。


當然,隨著事件的不同,對於訊息的解釋也有所不同,在C++Builder中也為各種常用的訊息定義了專屬的結構,你可以直接使用它們來解釋訊息。這些訊息定義在C++Builder目錄下的Include\vcl\messages.hpp中,你可以決定要自行解釋TMessage參數或是直接將其轉換成專屬的結構。很抽象嗎?我舉個例子吧,以WM_NCHITTEST訊息來說,C++Builder為它定義了TWMNCHitTest的專屬結構,所以你可以直接經由它來得到XPos、YPos等值。或者你也可以直接由TMessage的LParam取得其值,端看你使用的方便。仔細觀察TMessage及TWMNCHitTest兩個結構,你會發現它們是等價的,也就是說它們的大小是一致的,因此你可以直接用強制轉型互相轉換(這有點類似union的方法)。


struct TMessage

{

Cardinal Msg;

union

{

struct

{

Word WParamLo;

Word WParamHi;

Word LParamLo;

Word LParamHi;

Word ResultLo;

Word ResultHi;

};

struct

{

long WParam;

long LParam;

long Result;

};

};

};

struct TWMNCHitTest

{

Cardinal Msg;

long Unused;

union

{

struct

{

Windows::TSmallPoint Pos;

long Result;

};

struct

{

short XPos;

short YPos;

};

};

} ;


在收到訊息後,程式必須處理該訊息,若是不處理,則可直接將它交給Windows的內定處理程序來處理之,若是程式需要傳回值,也可以在此時傳回,Windows會將該值傳回給呼叫方。如此就完成了訊息傳遞的程序。


複雜嗎?一點也不!瞭解Windows訊息系統的運作後,我們來看看可以利用它來做些什麼有趣的事吧!


訊息使用範例一 使用者自定標題棒的實作


一般Windows程式的標題棒位於視窗的上方,我們可以利用該標題棒來移動視窗。以下我將為你示範如何利用C++Builder實作出置於視窗左方的標題棒。如圖一:




圖一 標題棒在左方的視窗。


如上圖,你可以很清楚地看到,這個視窗和其他的視窗有很大的不同;它的標題棒位於左方,而且其顏色為綠色,同時其文字的走向為由下而上的90度字形,而其功能則和一般的標題棒相同,你可以將滑鼠移至該處,然後移動該視窗。到底這是如何達成的呢?


WM_NCHITTEST訊息的奧秘


WM_NCHITTEST訊息是一個很特殊的訊息。它是用來決定目前滑鼠所在位置屬性的訊息,因此我們可以利用此特性,當滑鼠移至指定的位置時,傳回 HTCAPTION,使得系統以為滑鼠目前位於標題棒,如此你就可以移動視窗了。如何?是不是很神奇呢?


由上可知,只要我們適時地攔截WM_NCHITTEST訊息,然後傳回HTCAPTION,就可以順利地欺騙系統,達成在任何位置模擬出標題棒的效果。


C++ Builder的處理訊息的巨集


在C++Builder為了處理訊息的方便,因此定義了三個處理訊息的巨集(Macro)。


BEGIN_MESSAGE_MAP

MESSAGE_HANDLER(WM_NCHITTEST,TMessage,OnNcHitTest)

END_MESSAGE_MAP(TForm)


以上的三個巨集BEGIN_MESSAGE_MAP、MESSAGE_HANDLER及END_MESSAGE就是C++ Builder定義的巨集,其中比較重要的是MESSAGE_HANDLER;它共需要三個參數,第一個參數代表訊息的ID,第二個代表參數型態,最後一個則是訊息事件處理函數。


乍看之下,這個巨集似乎和MFC及OWL所使用的巨集有幾分神似,沒錯,不過其機制卻更為簡單及簡潔,我們可以看看C++Builder對於這三個巨集的原始定義:


#define BEGIN_MESSAGE_MAP virtual void __fastcall Dispatch(void *Message) \

{ \

switch (((PMessage)Message)->Msg) \

{

#define MESSAGE_HANDLER(msg,type,meth) \

case msg: \

meth(*((type *)Message)); \

break;

#define END_MESSAGE_MAP(base) default: \

base::Dispatch(Message); \

break; \

} \

}


相較於MFC或 OWL的可怕巨集,它實在是簡單多了,這是因為C++Builder已替你完成了大部份的工作。其實若我們把以上的巨集展開後,可以得到以下的結果:


virtual void __fastcall Dispatch(void *Message)

{

switch (((PMessage)Message)->Msg)

{

case WM_NCHITTEST:

OnNcHitTest(*((TMessage *)Message));

break;

default:

TForm::Dispatch(Message);

break;

}

}


怎麼樣?展開之後是不是有恍然大悟的感覺,要弄清楚這個巨集在賣啥膏藥是很容易的,如果你玩過MFC的訊息處理機制,再看到以上的巨集,相較之下,實在是小兒科,不過也就因其簡單,所以C++Builder的優勢益加彰顯。


我簡單地說明以上的程式:在每個TForm中都定義一個名為Dispatch的虛擬函式,它就是用來處理Windows的訊息的,在大部份情況下,訊息都是呼叫C++Builder所提供的處理函式,因此你不需要修改它。


換句話說,我們只要改寫Dispatch函式,就可以藉以處理指定的訊息了。前面提到的三個巨集只是將這個程序簡化而已,沒什麼大不了。


自定標題的繪製


由於我們要使用自定的標題,所以你必須將程式所使用的 TForm的BorderStyle性質設為 bsNone,如此你的TForm就不會有標題棒了。


再來你就必須自行繪製標題棒,我們希望繪製一個位於左於的標題,因此我們必須處理TForm的OnPaint事件,然後在此事件中繪製標題棒。以下即為其事件處理函式:


void __fastcall TForm1::FormPaint(TObject *Sender)

{

RECT rc;

::SetRect(&rc,0,0,ClientWidth,ClientHeight);

DrawButtonFace(Canvas,rc,1);

Canvas->Pen->Color=clGreen;

Canvas->Brush->Color=clGreen;

Canvas->Rectangle(0,0,20,ClientHeight);

:// 以下略去

:

}


你可以看到,我們畫出一個寬為20,顏色為綠色的標題棒。因此我們處理WM_NCHITTEST訊息的處理函式也必須做相對應的修改:


void __fastcall TForm1::OnNcHitTest(TMessage& Msg)

{

TPoint pt;

pt.x=LOWORD(Msg.LParam);

pt.y=HIWORD(Msg.LParam);

pt =ScreenToClient(pt);

RECT rc;

::SetRect(&rc,0,0,20,ClientHeight);

if (PtInRect(&rc,pt))

Msg.Result = HTCAPTION;

else

DefaultHandler(&Msg);

}


OnNcHitTest函式首先取得目前滑鼠所在點,注意,WM_NCHITTEST訊息所傳入的點為相對於螢幕的絕對座標,因此在取得該點後必須利用ScreenToClient函數將它轉為TForm的相對座標值,然後再據以判斷是否落於我們所定義的標題棒範圍內,若是則傳回HTCAPTION值,否則就交由內定的處理函式DefaultHandler來處理。如此就完成了一個位於左方的標題棒了。


旋轉文字的輸出


仔細觀察圖一,你會發現它所使用的標題字元的方向,已經因應標題棒的轉向而成為90旋轉的文字,這是如何達成的呢?


其實說穿了沒什麼,只是利用傳統SDK的繪圖方法來畫出來的。因為在C++Builder的TFont物件並沒有定義文字旋轉的屬性,所以我們只好透過傳統的GDI繪圖方法來達成這個目標。


char* msg=Caption.c_str();

LOGFONT fontRec;

memset(&fontRec,0,sizeof(LOGFONT));

fontRec.lfHeight = -13;

fontRec.lfWeight = FW_NORMAL;

fontRec.lfEscapement = 900; // 旋轉文字的關鍵

lstrcpy(fontRec.lfFaceName,"細明體");

HFONT hFont=CreateFontIndirect(&fontRec);

HFONT hOld=::SelectObject(Canvas->Handle,hFont);

::SetRect(&rc,0,0,20,ClientHeight);

::SetTextColor(Canvas->Handle,RGB(255,255,255));

::TextOut(Canvas->Handle,3,ClientHeight-3,msg,lstrlen(msg));

::SelectObject(Canvas->Handle,hOld);

::DeleteObject(hFont);


以上的程式我不打算詳加說明,簡單地說,它就是建立一個旋轉90度的字形,然後將字串以此字形畫於螢幕上,此段程式碼的關鍵在於你必須知道Canvas->Handle即是代表GDI繪圖的HDC。其餘的函式說明你都可以在一般講解傳統Windows SDK繪圖的書籍中找到。


由此我們也可以得到一個經驗:雖然C++Builder的快速程式發展環境已經取代了傳統SDK式的程式設計中大部份的工作,然而通曉一些必要的SDK程式技巧卻可以使你上一層樓。所以我建議你在『行有餘力』時,不妨可以看看SDK相關書籍,充實基礎知識。或許我們可以名之為『立足 BCB,放眼 SDK』的學習態度吧!


其他說明


在本程式中因為TForm的BorderStyle性質為bsNone。因此並沒有外框,為了美化視窗,所以我寫了幾個輔助函式來繪出立體框。若你在其他程式中有類似的需求,也可以使用之。


void DoRect(TCanvas* Canvas,RECT& rect,COLORREF cTopColor,COLORREF cBottomColor)

{

POINT p[3];

p[0].x = rect.right;

p[0].y = rect.top;

p[1].x = rect.left;

p[1].y = rect.top;

p[2].x = rect.left;

p[2].y = rect.bottom;

Canvas->Pen->Color=TColor(cTopColor);

Canvas->Polyline(p,3);

p[1].x = rect.right;

p[1].y = rect.bottom;

p[2].x--;

Canvas->Pen->Color=TColor(cBottomColor);

Canvas->Polyline(p,3);

}


void Frame3D(TCanvas* Canvas,RECT& rect,COLORREF cTopColor,COLORREF cBottomColor,int iColWidth)

{

rect.bottom--; rect.right--;

while (iColWidth > 0)

{

iColWidth--;

DoRect(Canvas,rect,cTopColor,cBottomColor);

InflateRect(&rect,-1,-1);

}

rect.bottom++; rect.right++;

}


void DrawButtonFace(TCanvas* Canvas,RECT& rect,int nBevelWidth)

{

Canvas->Brush->Color=clBtnFace;

Canvas->FillRect(TRect(rect));

Frame3D(Canvas,rect,::GetSysColor(COLOR_BTNSHADOW),::GetSysColor(COLOR_WINDOWFRAME),nBevelWidth);

Frame3D(Canvas,rect,::GetSysColor(COLOR_BTNHIGHLIGHT),::GetSysColor(COLOR_BTNSHADOW),nBevelWidth);

}


這三個函式中最重要的就是 DrawButtonFace,它是用來在一個矩形範圍中畫出一個類似Button的立體方框,在本程式中我用它來畫出TForm的邊框。你可以由圖一看出它的視覺效果。

程式的改進


前面我們提到改進bsNone視窗視覺效果的方式是利用自行撰寫的DrawButtonFace函式來達成,它雖不失為一個解決問題的方法,但是卻也因此增加了程式的複雜度,再來我為你示範一種利用改寫CreateParams函式的技巧來達成類似功能的方法。


CreateParams是一個虛擬函式,你可以經由它來修改windows的style,因為原先在C++Builder中所定義的Form是一個Dialog(交談窗),而交談窗的外形內定是有標題棒的,然而如果我們如前面的方法將外框設為bsNone 的話,那就必須自行畫出假的視窗外框,否則看起來不好看。


但是在Windows系統中除了前面的Dialog式的視窗之外,還提供了另一種POPUP式的視窗,只不過在C++Builder並未提供該選項罷了。因此我們其實可以透過改寫CreateParams的方式來產生WS_POPUP形式的視窗,如此一來我們就不必煞費周章地撰寫畫外框的函式了。它的程式其實很簡單,只是將Params.Style的WS_DLGFRAME (代表使用Dialog外框),改成另一種WS_POPUP (彈出式視窗)。要做到以上效果,只要利用and及or運算就可以達到了。以下即為其程式碼:


void __fastcall TForm1::CreateParams(TCreateParams& Params)

{

TForm::CreateParams(Params);

Params.Style and = WS_POPUP;

Params.Style ^= WS_DLGFRAME;

}


圖二為改寫後的程式執行結果,不僅程式簡潔了許多,而且外觀也較好看,那是因為我們在畫標題棒時,不會像前面一樣將外框蓋住的緣故。




圖二 利用CreateParams技巧的新程式。


訊息使用範例二 在程式中使用材質背景


許多人在使用網際網路瀏覽器如Internet Explorer、Netscape上網站時,會發現許多網頁上普遍使用了材質圖案做為背景,大大加強了它的視覺效果,也使用網頁看起來更為美侖美奐,這時也許你會想:這個材質背景是如何做出來的呢?


在以下的文章中,我會示範如何利用C++Builder做出上述的材質背景效果,讓你的程式也可以做出如Browser般的效果。此程式的執行效果如圖三




圖三 具有材質片背景的Form


WM_ERASEBKGND訊息說明


WM_ERASEBGGN是在Windows背景將要被清除時,所觸發的訊息。在此訊息發生時,會傳入要清除的Windows的HDC ( 還記這個SDK中用來繪圖的重要角色吧?)。因此我們可以取得此HDC,然後將Canvas的Handle值設為該值,如此便可以在Canvas上作畫了。

宣告使用WM_ERASEGKGN


class TForm1 : public TForm

{

__published: // IDE-managed Components

TPanel *Panel1;

private: // User declarations

public: // User declarations

__fastcall TForm1(TComponent* Owner);

void virtual __fastcall OnWMEraseBkgnd(TWMEraseBkgnd& Msg);

BEGIN_MESSAGE_MAP

MESSAGE_HANDLER(WM_ERASEBKGND,TWMEraseBkgnd,OnWMEraseBkgnd)

END_MESSAGE_MAP(TForm)

};


為了要攔截WM_ERASEBMGN訊息,因此我們必須利用前面談過的巨集來宣告之,在此我們採用C++Builder為WM_ERASEBKGND定義的TWMEraseBkgnd訊息結構做為參數,同時定義了一個訊息處理函數 OnWMEraseGkgnd。當然,它所傳入的訊息參數是前述的TWMEraseBkgnd&。


以材質圖案填滿畫面


在完成了訊息處理函數的定義之後,再來我們就必須撰寫實際的程式碼。為了在所傳入的HDC中做畫,我們必須new一個 Canvas,然後自TWMEraseBkgnd中取得HDC的值。


接著為了要將材質背景載入,我們必須new一個Graphics::TBitmap (在這裏加上Graphics:: 是因為尚有另一種Tbitmap ,是位於Windows的namespace中的,因此必須以Graphics:: 來區別它的名稱空間)。然後,我們就可以利用LoadFromFile將材質背景圖案載入。


在完成了以上兩個必要的準備動作之後,我們就可以正式將材質背景畫在Canvas上面了,首先當然要計算所畫的次數,然後利用迴圈以Canvas的Draw指令,將它填滿整個螢幕。最後不要忘了將new產生的物件,以delete刪除之。以下為其程式列表:


void __fastcall TForm1::OnWMEraseBkgnd(TWMEraseBkgnd& Msg)

{

TCanvas* canvas = new TCanvas;

Graphics::TBitmap* bitmap = new Graphics::TBitmap;

bitmap->LoadFromFile("back.bmp");

canvas->Handle = Msg.DC;

int cx = ClientWidth/bitmap->Width + 1;

int cy = ClientHeight/bitmap->Height + 1;

for (int i=0; i<cy; i++)

for (int j=0; j<cx; j++)

canvas->Draw(j*bitmap->Height,i*bitmap->Width,bitmap);

Msg.Result = true;

delete bitmap;

delete canvas;

}


怎麼樣?不錯吧!其實只要多多充實關於Windows訊息的知識,雖然在C++Builder中直接使用Windows訊息的機會並不多,但是在某些時候,它卻可以發揮小兵立大功的效果,在像本節所舉的例子一般。

結論


在本文中我為你示範了在C++Builder中處理訊息的方法,同時以一個實際的自定標題棒視窗及材質背景圖為範例,仔細說明了其中之技巧。除此之外,在Windows系統中,訊息(Message)是無所不在的,它是許多傳統的視窗元件用以互相溝通的元件,因此除非你能保證你永遠不會使用到別的標準元件,否則你就必須具備訊息處理的能力。所以說,瞭解Windows訊息是你不可或缺的技巧,唯有如此,你才能『百尺竿頭,更進一步』,不會被RAD給局限住。
begwolf 2000-06-07
  • 打赏
  • 举报
回复
<创世纪的C++ Builder>??这是网站还是光盘?
wxz 2000-06-07
  • 打赏
  • 举报
回复
<创世纪的C++ Builder>中有详细的例子,仔细看看吧
kxy 2000-06-06
  • 打赏
  • 举报
回复
yanzt要的是改变Form的caption的东西,
比如在Form的Caption上加一个Button,Caption的蓝条换成一副图等等.
我想如果直接做是比较麻烦的.用控件比较方便,何况此控件有Source
可以学习他们是如何做的.:)
alexela 2000-06-06
  • 打赏
  • 举报
回复
我劝yanzt老兄你最好学会在程序中控制动态修改FORM属性,因为这很简单,没必要专门为此而使用组件(反而不方便)。你遇到的问题,在于组件设计者考虑设计阶段(DESIGNER)改变而忽视运行阶段。我不知道你要实现什么目的,否则我可帮你。
kxy 2000-04-03
  • 打赏
  • 举报
回复
我把一个demo和help发给你,
kxy 2000-04-03
  • 打赏
  • 举报
回复
按alt+F4可以终止程序呀,
此时程序的终止要自己控制,或者放一个它的close Button
你在上面放按钮了吗?
kxy 2000-04-03
  • 打赏
  • 举报
回复
我在delphi中使用,挺好的,C++Builder我没有安装。
不是有source吗?你可以跟踪看看。
我不记得是从哪里down的了,好像是 delphi super page

13,825

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧