控件什么时候拥有了Canvas??? (已经解答得很好,散分) (300分)

如果你写一个控件
然后在 constructor Create...
里调用
Canvas.TextWidth(...)
看看会怎么样? 解释一下。
 
那还需要看你的控件是从哪里继承下来的,如果是从TObject继承下来,那这样做就会....[[:D]
 
大部分情况下,控件是指TControl,有时候甚至特指TWinControl。非可视控件从
TComponent继承,有人称为部件。

如果在Create里调用Canvas,绝大部分情况下会出现无法预料的错误,例如越权访问。有
时甚至会出现编译错误。如果不出现编译错误,说明Canvas对该控件来说是有效的。只是
Create即使完成了也一般还不能建立Canvas,因为窗口句柄还没有被分配。

这样的问题你看看TControl/TWinControl的源码就什么都明白了。
 
如果一定要用TextWidth,你只能建立一个兼容的内存设备上下文,然后取TextWidth。
在Delphi中的做法是建立一个TBitmap,用TBitmap的Canvas。
 
谢谢各位的大力相助,还是挺复杂的。
>> 只是Create即使完成了也一般还不能建立Canvas,因为窗口句柄还没有被分配。
Canvas在何时建立? 窗口句柄何时被分配?
 
拥有DC是很耗资源的
 
偶有内存,不怕.
 
看来Leecharge都说了
总而言之,高手想怎么做都行
 
我觉得写Delphi编译器的也是高手啊.
可我就是不明他们的代码,都是高手,该听谁的呢?
 
我好象明白了楼主的意思。是不是说对一个含有Canvas的控件,在控件创建后,什么时候可以使用Canvas。楼主可能是在控件的Create事件中使用Canvas,结果失败。其实,在Create事件中,只能给控件赋值,以便创建控件。该事件结束后,控件才得以建立,所以Canvas是不能使用的。可以在OnActivate事件中使用。
 
控件是否会多次响应OnActivate事件?
这样的我初始值要怎么设置?
 
wlmmlw

写Delphi编译器的是安德森(borland的创始人之一,borland的首席程序员,因为领导层的错误方针被迫跳槽[虽然他走的时候可能很高兴,但我只能接受被迫这个事实]),被
microsoft以300m/年挖走了,然后就有了,v j++这个烂东西(不得不承认的是,他的编译器到现在都是java的执行效率最高的),c#
 
所有可见的控件均包含canvas属性
但在一部分控件被隐藏了起来,

上面不是有位仁兄说吗,
//Canvas就是Windows API中的设备上下文句柄的封装而已。不过我更喜
//欢直接用GDI的API
有些控件就是采用GDI的API对图形界面直接进行设计的,所以他们摒弃了
父类的Canvas属性,或者压根就从没有Canvas属性的父类那儿继承
这类控件包括Button等大部分标准windows控件,
但是GDI函数需要一个句柄传递到图形设备,提供这个句柄自然可以实现对应

//delphi提供两个类结构用于自定义控件的设置
TGraphicControl,,,无句柄
TCustomControl,,,有句柄
 
谢谢!
如果直接用GDI API那写一个程序不是要花很多时间?
有没有这方面的学习资料?
 
就Canvas能够完成的功能来说,用GDI来做,工作量几乎是一样的。
 
如果不是写底层的应用,没必要使用GID
我已经说了
//delphi提供两个类结构用于自定义控件的设置
TGraphicControl,,,无句柄
TCustomControl,,,有句柄

如果想自定义控件,就从这里继承吧

这只是两张白纸,可以绘出最新最美的图画!
 
不知道大家有没有试过用TImagList.Draw
速度好快,比一般的画图快N倍.
 
TImageList.Draw是从TCustomImageList.DoDraw中调用Windows内核的COMMCTRL.DLL中方法绘制,是Windows核心代码,比我们通常的GDI函数调用(TCanvas实际上是GDI的封装)实现的绘制快。
MS正在推新一代GDI+,功能速度都有改善。
 
apw:高手!
 
楼主的意思可能是想从一个不带TCanvas的类继承,创建新的有TCanvas的控件吧。

------------------------------------------------------
VCL建立TCanvas的标准方法:
Windows Message: WM_PAINT -> WMPaint(var Message : TWMPaint) -> PaintHandler -> PaintWindow(DC HDC) -> Paint

------------------------------------------------------
要自己从一个不带TCanvas的控件上继承,而又希望用TCanvas,标准的方法为(可从TControl开始继承):
1.创建TCanvas
constructor TMyControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCanvas := TControlCanvas.Create;
TControlCanvas(FCanvas).Control := Self;
end;

destructor TMyControl.Destroy;
begin
if CaptureControl = Self then SetCaptureControl(nil);
FCanvas.Free;
inherited Destroy;
end;

2.响应WM_PAINT
{ 不带缓冲的绘制方法 }
procedure TMyControl.WMPaint( var Message : TWMPaint );
var
PS : TPaintStruct;
DC : HDC;
begin
PaintHandler(Message); // 通过PaintHandler开始绘制
end;

{ 带缓冲的绘制方法 }
procedure TMyControl.WMPaint( var Message : TWMPaint );
var
DC, MemDC: HDC;
MemBitmap, OldBitmap: HBITMAP;
PS: TPaintStruct;
begin
DC := GetDC(0); // 取DC
// 创建绘制缓冲区
MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom);
ReleaseDC(0, DC);
MemDC := CreateCompatibleDC(0);
// 指定对MemDC缓制到缓冲区上
OldBitmap := SelectObject(MemDC, MemBitmap);
try
// 开始绘制
DC := BeginPaint(Handle, PS);
// 擦清缓冲上的内容
Perform(WM_ERASEBKGND, MemDC, MemDC);
// 绘制内容
PaintHandler(Message);
// 将缓冲区上复制到界面上
BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY);
// 绘制结束
EndPaint(Handle, PS);
finally
// 恢复MemDC
SelectObject(MemDC, OldBitmap);
DeleteDC(MemDC);
// 释放缓冲区
DeleteObject(MemBitmap);
end;
end;

3.PaintHandler用于根据控件的状态决定绘制边框等操作
procedure TMyControl.PaintHandler(var Message: TWMPaint);
var
DC: HDC;
PS: TPaintStruct;
begin
DC := Message.DC;
if DC = 0 then DC := BeginPaint(Handle, PS);
try
PaintWindow(DC);
finally
if Message.DC = 0 then EndPaint(Handle, PS);
end;
end;

4.创建TCanvas准备绘制
procedure TMyControl.PaintWindow(DC: HDC);
FCanvas : TCanvas;
begin
FCanvas.Lock;
try
FCanvas.Handle := DC;
try
TControlCanvas(FCanvas).UpdateTextFlags;
Paint;
finally
FCanvas.Handle := 0;
end;
finally
FCanvas.Unlock;
end;
end;

5.绘制
procedure TMyControl.Paint;
begin
// 绘制
end;

------------------------------------------------------
一个简单的实现方法:
1.创建TCanvas
constructor TMyControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCanvas := TControlCanvas.Create;
TControlCanvas(FCanvas).Control := Self;
end;

destructor TMyControl.Destroy;
begin
if CaptureControl = Self then SetCaptureControl(nil);
FCanvas.Free;
inherited Destroy;
end;

2.响应WM_PAINT
procedure TMyControl.WMPaint( var Message : TWMPaint );
var
DC: HDC;
PS: TPaintStruct;
begin
DC := Message.DC;
if DC = 0 then DC := BeginPaint(Handle, PS);

FCanvas.Lock;
try
FCanvas.Handle := DC;
try
TControlCanvas(FCanvas).UpdateTextFlags;
Paint;
finally
FCanvas.Handle := 0;
end;
finally
begin
if Message.DC = 0 then EndPaint(Handle, PS);
FCanvas.UnLock;
end;
end;
end;

------------------------------------------------------
Invalidate实际上封装了Windows GDI API内的InvalidateRect方法来重绘整个控件区域,基本上等价于GDI API的InvalidateRect(Handle, BoundsRect, true);
要提高控件的重绘速度,就应该确定哪些区域需要重新绘制,再主动调用InvalidateRect来进行,而尽量少用或不用Invalidate或Repint.

 

Similar threads

回复
0
查看
686
不得闲
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
顶部