組件是如何顯示出來的? VCL組件開發及應用
http://www.delphi2007.net/DelphiVCL/html/delphi_20061225141614123.html
同是一個基類比如twinctrol派生出來的的控制項,顯示的樣子各不相同,比如說BUTTON和STRINGGRID,看了看CREATE函數,好像裡邊只是對基本參數賦初試值,那當一個控制項被放置到視窗上後即被顯示出來,它是如何顯示出來的,這個過程怎樣處理的?換句話說它都調用了什麼函數實現顯示控制項的,控制項的樣式又是如何更改成各式各樣的?自己如何封裝才能讓一個控制項按自己的樣式顯示出來?
解答你的問題可以寫書了,建議看看入門書或者大全書,就瞭解了
一般通過調用TCanvas的方法或是GDI的API函數畫出的介面。
這個要做的多了畫起來就得心應手了,一般可以去找網上很多的時間日期控制項,有很多比較簡單的,很容易看懂,然後就照著他的畫,你想畫什麼樣的就畫什麼樣的,呵呵
To: maozefa(阿發伯)
我知道問的問題有點範圍太大了,入門的書也看了一點,比如inside vcl、高手突破、D5指南什麼的,只看明白了百分之70左右,總是感覺他講什麼知道,但是只是理性上的認識,沒有感性認識,或者說,明白其中的道理,但是讓自己作就又不知道如何下手了,希望可以找到以一個控制項比如BUTTON為主線講其整個實現過程。跟過源碼,但是總是無攻而返。能不能再給建議一些符合我情況要求的基本的書。順便問一下,有沒有專門介紹關於組件開發的書籍呢?還有關於DELPHI詳細文法的書籍,因為看VCL時發現一些文法書上根本就沒有介紹,比如說DEFAULT關鍵字等,看英文協助總是只能看董個大概,對所介紹知識的靈活運用協助不大,所以希望能找到一本詳細點的書籍。
To:zhangcheng125(老狼)
你說的時間控制項能不能給說一個具體的名稱,我好搜尋以下,謝謝。感覺寫控制項這個工作還是需要windowsAPI的知識比較多。
其實寫控制項並不是很難的東西,只是要學好畫圖的,像DELPHI裡的TCANVAS類,還有就是WINDOWS API中關於GDI的部份,至於時間控制項,在DELPHI盒子裡很多的,可以去下來看看。個人認為只要把畫圖的這方面的知識學好了,管他什麼組件都可以寫出來(組件都是畫出來的。:)
下班了,粗粗的寫了一個,希望對你有協助。
unit TestButtons;
interface
uses
SysUtils, Classes, Controls, Buttons, Windows, Messages, Graphics;
type
TTestButton = class(TCustomControl)
private
FShowEdge: Boolean;
FEdgeColor: TColor;
FEdgeWidth: Integer;
procedure SetShowEdge(const Value: Boolean);
procedure SetEdgeColor(const Value: TColor);
procedure SetEdgeWidth(const Value: Integer);
{ Private declarations }
protected
{ Protected declarations }
procedure Paint; override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
published
{ Published declarations }
property Caption;
property Color;
property Font;
property ShowEdge: Boolean read FShowEdge write SetShowEdge default True;
property EdgeColor: TColor read FEdgeColor write SetEdgeColor default clBlack;
property EdgeWidth: Integer read FEdgeWidth write SetEdgeWidth default 1;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TTestButton]);
end;
{ TTestButton }
constructor TTestButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FShowEdge := True;
FEdgeColor := clBlack;
FEdgeWidth := 1;
end;
procedure TTestButton.Paint;
var
R: TRect;
procedure DrawTexts(AText: string; ARect: TRect);
begin
DrawText(Canvas.Handle, PChar(AText), -1, ARect, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
end;
begin
inherited Paint;
R := ClientRect;
Canvas.Brush.Color := Color;
Canvas.Brush.Style := bsSolid;
Canvas.Pen.Width := EdgeWidth;
Canvas.Pen.Color := EdgeColor;
Canvas.Pen.Style := psSolid;
if ShowEdge then
Canvas.Rectangle(ClientRect);
InflateRect(R, -EdgeWidth, -EdgeWidth);
Canvas.Font.Assign(Font);
DrawTexts(Caption, R);
end;
procedure TTestButton.SetEdgeColor(const Value: TColor);
begin
if Value <> FEdgeColor then
begin
FEdgeColor := Value;
Repaint;
end;
end;
procedure TTestButton.SetEdgeWidth(const Value: Integer);
begin
if Value <> FEdgeWidth then
begin
FEdgeWidth := Value;
Repaint;
end;
end;
procedure TTestButton.SetShowEdge(const Value: Boolean);
begin
if Value <> FShowEdge then
begin
FShowEdge := Value;
Repaint;
end;
end;
end.
TO:zhangcheng125(老狼)
謝謝,很有協助,藉機會問個問題:
property ShowEdge: Boolean read FShowEdge write SetShowEdge default True;
property EdgeColor: TColor read FEdgeColor write SetEdgeColor default clBlack;
property EdgeWidth: Integer read FEdgeWidth write SetEdgeWidth default 1;
中都有個default關鍵字,協助中說不是預設值,是用來決定是否儲存屬性值,不太理解什麼含義,麻煩給解釋一下。
自己試著寫個控制項:主要是畫個類似GRID控制項。
具體做法是在TScrollBox控制項中來畫,代碼如下:
unit test;
interface
uses
Windows, Controls, Forms, Classes, Graphics, Messages, dialogs;
type
TStatu = (bgNormal, bgSelected, bgMouseIn);
TCellInfo = record
Rect: TRect;
Index: Integer;
Status: TStatu;
end;
TBrxDrawGrid = class(TGraphicControl)
private
FAlign: TAlign;
FContainer: TScrollBox;
FMax: Integer;
FRow: Integer;
FCol: Integer;
FCellWidth: Integer;
FCellHeight: Integer;
FSpRow: Integer;
FSpCol: Integer;
FClientWidth: Integer;
FClientHeight: Integer;
FCIArray: Array of Array of TCellInfo;
procedure SetAlign(Value: TAlign);
procedure SetMax(const Value: Integer);
function GetMax: Integer;
procedure SetRow(const Value: Integer);
procedure SetCol(const Value: Integer);
procedure SetCellWidth(const Value: Integer);
procedure SetCellHeight(const Value: Integer);
procedure SetSpRow(const Value: Integer);
procedure SetSpCol(const Value: Integer);
procedure SetClientHeight(const Value: Integer);
procedure SetClientWidth(const Value: Integer);
procedure InitCIArray(const Row, Col: Integer);
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
protected
procedure SetCellInfo(const Row, Col: Integer);
procedure DrawCell;
published
property Align: TAlign read FAlign write SetAlign default alNone;
property RowCount: Integer read FRow write SetRow default 5;
property ColCount: Integer read FCol write SetCol default 5;
property DefaultColWidth: Integer read FCellWidth write SetCellWidth default 64;
property DefaultRowHeight: Integer read FCellHeight write SetCellHeight default 24;
property SpaceOfRow: Integer read FSpRow write SetSpRow default 1;
property SpaceOfCol: Integer read FSpCol write SetSpCol default 1;
property Height: Integer read FClientHeight write SetClientHeight default 120;
property Width: Integer read FClientWidth write SetClientWidth default 320;
public
constructor Create(AOwner: TComponent); override;
procedure Paint; override;
function GetCellCount: Integer;
function GetHandle: HWND;
end;
implementation
{ TBrxDrawGrid }
constructor TBrxDrawGrid.Create(AOwner: TComponent);
var
Max: Integer;
begin
inherited Create(AOwner);
FContainer := TScrollBox.Create(AOwner);
FContainer.Parent := TWinControl(AOwner);
FContainer.Align := alNone;
SetRow(5);
SetCol(5);
SetCellWidth(64);
SetCellHeight(24);
SetSpRow(1);
SetSpCol(1);
Max := 25;
SetMax(Max);
InitCIArray(5, 5);
end;
procedure TBrxDrawGrid.DrawCell;
var
i, j: Integer;
begin
Canvas.Handle := GetDC(FContainer.Handle);
Canvas.Pen.Color := clRed;
Canvas.Brush.Color := clBlue;
for i := 0 to FRow - 1 do
begin
for j := 0 to FCol - 1 do
begin
Canvas.Rectangle(FCIArray[i][j].Rect);
end;
end;
end;
function TBrxDrawGrid.GetCellCount: Integer;
begin
Result := GetMax;
end;
function TBrxDrawGrid.GetHandle: HWND;
begin
Result := FContainer.Handle;
end;
function TBrxDrawGrid.GetMax: Integer;
begin
Result := FMax;
end;
procedure TBrxDrawGrid.InitCIArray(const Row, Col: Integer);
var
i, j: Integer;
begin
SetLength(FCIArray, Row, Col);
for i := 0 to Row - 1 do
begin
for j := 0 to Col - 1 do
begin
FCIArray[i][j].Rect.Left := j * (FCellWidth + FSpCol);
FCIArray[i][j].Rect.Top := i * (FCellHeight + FSpRow);
FCIArray[i][j].Rect.Right := FCIArray[i][j].Rect.Left + FCellWidth;
FCIArray[i][j].Rect.Bottom := FCIArray[i][j].Rect.Top + FCellHeight;
FCIArray[i][j].Index := i * Col + j;
FCIArray[i][j].Status := bgNormal;
end;
end;
end;
procedure TBrxDrawGrid.Paint;
begin
inherited;
DrawCell;
end;
procedure TBrxDrawGrid.SetAlign(Value: TAlign);
begin
if FAlign <> Value then
begin
FAlign := Value;
FContainer.Align := FAlign;
end;
end;
procedure TBrxDrawGrid.SetCellHeight(const Value: Integer);
begin
if FCellHeight <> Value then
begin
FCellHeight := Value;
InitCIArray(FRow, FCol);
end;
end;
procedure TBrxDrawGrid.SetCellInfo(const Row, Col: Integer);
begin
end;
procedure TBrxDrawGrid.SetCellWidth(const Value: Integer);
begin
if FCellWidth <> Value then
begin
FCellWidth := Value;
InitCIArray(FRow, FCol);
end;
end;
procedure TBrxDrawGrid.SetClientHeight(const Value: Integer);
begin
if FClientheight <> Value then
begin
FClientHeight := Value;
FContainer.Height := FClientHeight;
end;
end;
procedure TBrxDrawGrid.SetClientWidth(const Value: Integer);
begin
if FClientWidth <> Value then
begin
FClientWidth := Value;
FContainer.Width := FClientWidth;
end;
end;
procedure TBrxDrawGrid.SetCol(const Value: Integer);
begin
if FCol <> Value then
begin
FCol := Value;
InitCIArray(FRow, FCol);
end;
end;
procedure TBrxDrawGrid.SetMax(const Value: Integer);
begin
if FMax <> Value then
begin
FMax := Value;
end;
end;
procedure TBrxDrawGrid.SetRow(const Value: Integer);
begin
if FRow <> Value then
begin
FRow := Value;
InitCIArray(FRow, FCol);
end;
end;
procedure TBrxDrawGrid.SetSpCol(const Value: Integer);
begin
if FSpCol <> Value then
begin
FSpCol := Value;
InitCIArray(FRow, FCol);
end;
end;
procedure TBrxDrawGrid.SetSpRow(const Value: Integer);
begin
if FSpRow <> Value then
begin
FSpRow := Value;
InitCIArray(FRow, FCol);
end;
end;
procedure TBrxDrawGrid.WMPaint(var Message: TWMPaint);
begin
Paint;
end;
end.
碰到幾個問題:
1.我的思路是系統發生WMPaint事件,調用Paint函數,Paint函數調用DrawCell,DrawCell完成畫圖工作並提供介面實現OnDrawCell。實現之後,當表單最小化再開啟,或是被其他船體覆蓋後,所畫的圖形消失,並沒有重畫,分析原因是因為沒有產生WM_PAINT訊息,控制項部分被擋住,當部分失效區有效時應該會產生個訊息,應該對這個訊息作處理,但是查看VCL中TDRAWGRID的實現,沒有弄明白他這一點是如何作的。
2.但寬度大於容器(TScrollBox)時,如何讓其產生捲軸。
3.以上諸如此類的問題,通過學習什麼書籍可以掌握解決方案?
謝謝
首先說明一下Default的問題,Default 是指將Default後面的值儲存到表單的dfm檔案中並不是指定預設值,如果要指定預設值的話要在建構函式中給定。
--------------------------
1.重繪的問題中你不用在WMPAINT訊息下調用Paint函數,WMPAINT函數本來就會調用PAINT函數的。還有就是Canvas本就是TGraphicControl的一個屬性,所以直接用就行了,不用去寫這個:Canvas.Handle := GetDC(FContainer.Handle);了,再就是我不懂你為什麼要選建立個FContainer,直接畫不就行了。
2.關於捲軸。我一般是這麼做的自繪捲軸,比方說垂直的捲軸,道理是:
12321321
32323232 ----------top
43434343 捲軸
43434343 ----------Bottom
根據當前捲軸滑動的距離把頭部不顯示出來的畫在顯示地區外,也就是畫的時候:比方一個地區TRect,那麼就設他的Top為負的,這樣就能把不顯示的畫在地區外面,(這要根據計算捲軸滑塊滑動距離來得出TOp為負多少)同理水平捲軸也一樣。
3.諸如此類問題,最好的學習是看別人的原始碼,我是看了一些基礎的原始碼後自己在實踐中摸索的一些東西
以上個人覺的有些沒講清楚,也可能是錯的(全是我自己實踐中嘗試出來的,不過還算行還能用,呵呵),有不當或是不清楚的地方還望樓主和各位見諒,有空我可以寫個簡單的捲軸的例子給樓主。
學習
TO:zhangcheng125(老狼)
"有不當或是不清楚的地方還望樓主和各位見諒"---這我可不敢當,本來就是在向你請教問題,你能回答已經感不盡了。至於Default的問題現在脫您的富很清楚了,讀中文就是舒服,協助可以看懂,但是理解的不透徹。但是還有很多文法好笑一般文法書中也都沒有提到過,比如重新聲明什麼的,不知道你看的是哪本基礎的文法書籍,給推薦一下吧 :)
1.我故意用WMPAINT調用PAINT是因為我看了一下DRAWGRID的VCL源碼,它的WMPAINT好像沒有調用PAINT函數,(也可能在更早的父代對象中的WMPAINT中就調用了,但是我沒有跟蹤出來,沒有掠清頭緒,乾脆就覆寫了它),在PAINT中相應ONDRAWCELL,但是發現系統就沒有響應WMPAINT,正在鬱悶ing中,想不明白怎麼連一個WMPAINT訊息都沒有產生,或者說沒有相應。(之所以說沒有產生wmpaint訊息,是因為其中代碼根本沒執行)。各位知道是什麼原因嗎?
“Canvas.Handle := GetDC(FContainer.Handle);了,再就是我不懂你為什麼要選建立個FContainer,直接畫不就行了。”--因為FContainer這個本身是一個TScrollBox對象執行個體。我的想法是將表格畫到這個控制項裡,本來開始想採用TPANEL,但是他沒有捲軸,所以就選擇了TScrollBox.雖然CANVAS被封裝可以直接使用,但是不將控制代碼給他就畫不到對應控制項上。不直接畫是因為可以省去一些屬性的實現,比如說ALIGN等等,而且他有捲軸,可以直接拿來用,捨得自己畫還要做更多的工作。
2.你說的捲軸的實現方法,我是聽董了,但是圖沒有看董,還有就是我想要是真的實現起來可能還會有接連不斷的問題隨之而來,不是怕遇到問題,只是想先解決目前的問題。
最後,謝謝zhangcheng125(老狼),目前還不會結帖,因為還有很多問題困擾。所以分遲遲不能發,請見諒。不過我知道您也不是為了分數對吧:),只是我心裡過不去。哈哈,還希望以後多多指教。
今天又確認了一下,其他從TGraphicControl派生下來的控制項比如說TBevel,就是通過改寫Paint方法來實現繪製的,我直接派生了一個,只改寫了Paint方法,但是結果是又是會響應WMPAINT訊息,有時又不會,趕到納悶。在回到原來的控制項上來,同樣也是改寫了Paint方法,為什麼就不行呢,希望高人指點。
問題解決,開始是我有些基本屬性沒有初始化,初始化後問題解決。還有前面zhangcheng125(老狼)所說的Canva可以直接使用,是我多此一舉了。
最後再次感謝zhangcheng125(老狼),以後還請多指教。