先擦除背景:

procedure TCustomForm.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
if not IsIconic(Handle) then inherited
else
begin
Message.Msg := WM_ICONERASEBKGND;
DefaultHandler(Message);
end;
end; procedure TWinControl.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
with ThemeServices do
if ThemesEnabled and Assigned(Parent) and (csParentBackground in FControlStyle) then
begin
{ Get the parent to draw its background into the control's background. }
DrawParentBackground(Handle, Message.DC, nil, False);
end
else
begin
{ Only erase background if we're not doublebuffering or painting to memory. }
if not FDoubleBuffered or
(TMessage(Message).wParam = TMessage(Message).lParam) then
FillRect(Message.DC, ClientRect, FBrush.Handle); // Brush的颜色事先读取好了
end; Message.Result := ;
end;

然后进行绘制(背景色已经事先存在,无论后面绘制了什么都不影响背景色,如果不绘制,就全部都是背景色):

procedure TCustomForm.WMPaint(var Message: TWMPaint);
var
DC: HDC;
PS: TPaintStruct;
begin
if not IsIconic(Handle) then
begin
ControlState := ControlState + [csCustomPaint]; // 模仿1
inherited; // 模仿2
ControlState := ControlState - [csCustomPaint];
end
else
begin
DC := BeginPaint(Handle, PS);
DrawIcon(DC, , , GetIconHandle);
EndPaint(Handle, PS);
end;
end;

inherited会调用:

procedure TWinControl.WMPaint(var Message: TWMPaint);
var
DC, MemDC: HDC;
MemBitmap, OldBitmap: HBITMAP;
PS: TPaintStruct;
begin
if not FDoubleBuffered or (Message.DC <> ) then
begin
if not (csCustomPaint in ControlState) and (ControlCount = ) then
inherited
else
PaintHandler(Message); // 走这里
end
else
begin
DC := GetDC();
MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom);
ReleaseDC(, DC);
MemDC := CreateCompatibleDC();
OldBitmap := SelectObject(MemDC, MemBitmap);
try
DC := BeginPaint(Handle, PS);
Perform(WM_ERASEBKGND, MemDC, MemDC);
Message.DC := MemDC;
WMPaint(Message);
Message.DC := ;
BitBlt(DC, , , ClientRect.Right, ClientRect.Bottom, MemDC, , , SRCCOPY);
EndPaint(Handle, PS);
finally
SelectObject(MemDC, OldBitmap);
DeleteDC(MemDC);
DeleteObject(MemBitmap);
end;
end;
end;

继续:

procedure TWinControl.PaintHandler(var Message: TWMPaint);
var
I, Clip, SaveIndex: Integer;
DC: HDC;
PS: TPaintStruct;
begin
DC := Message.DC;
if DC = then DC := BeginPaint(Handle, PS);
try
if FControls = nil then PaintWindow(DC) else
begin
SaveIndex := SaveDC(DC);
Clip := SimpleRegion;
for I := to FControls.Count - do
with TControl(FControls[I]) do
if (Visible or (csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
(csOpaque in ControlStyle) then
begin
Clip := ExcludeClipRect(DC, Left, Top, Left + Width, Top + Height);
if Clip = NullRegion then Break;
end;
if Clip <> NullRegion then PaintWindow(DC); // 走这里
RestoreDC(DC, SaveIndex);
end;
PaintControls(DC, nil);
finally
if Message.DC = then EndPaint(Handle, PS);
end;
end;

TCustomForm有相应的覆盖函数:

procedure TCustomForm.PaintWindow(DC: HDC); // 模仿3
begin
FCanvas.Lock;
try
FCanvas.Handle := DC;
try
if FDesigner <> nil then FDesigner.PaintGrid else Paint; // 模仿4
finally
FCanvas.Handle := ;
end;
finally
FCanvas.Unlock;
end;
end;

Paint会调用:

procedure TCustomForm.Paint; // Paint是dynamic函数,也是虚函数
begin
if Assigned(FOnPaint) then FOnPaint(Self); // 巨变:这里直接调用程序员事件,而不是等着程序员覆盖Paint函数(那样做也可以,另外还可直接覆盖PaintWindow虚函数,所以一共有3种方法,即:覆盖OnPaint事件,覆盖PaintWindow虚函数,覆盖Paint虚函数)
end;

这个FOnPaint来自:

    property OnPaint: TNotifyEvent read FOnPaint write FOnPaint stored IsForm;

它会调用我写的事件内容:

procedure TForm1.FormPaint(Sender: TObject);
begin
//
end;

即使为空,也丝毫不影响整个Form1的显示。也许像上面那样写会被编译器删除,那么我这样写:

procedure TForm1.FormPaint(Sender: TObject);
begin
tag := ;
end;

还是丝毫不影响整个Form1的显示。为什么会不影响呢?因为背景色提前就绘制在上面了,后面的OnPaint无论是否绘制,都不影响它的存在,顶多覆盖一小部分区域。比如:

procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.Brush.Color := clRed;
Canvas.Rectangle(, , , );
end;

也就是覆盖了一个角,剩下的还是背景色。

---------------------------------------------------------------------

这里测试了覆盖Paint函数,OnPaint的代码保留,但是效果只有左上角一个小绿块,而没有红色方块。如果加上inherited(IDE会自动帮你加上,也就是推荐使用),那么红的绿的方块都有,比较有意思:

procedure TForm1.Paint;
begin
// inherited;
Canvas.Brush.Color := clGreen;
Canvas.Rectangle(, , , );
end;

---------------------------------------------------------------------

唯一有个问题是,InitInheritedComponent读取dfm的颜色以后,是什么时候把它赋值给FBrush.Color的?它与{$R *.dfm}是什么关系?

研究一下TForm.WMPaint过程(也得研究WM_ERASEBKGND)——TForm虽然继承自TWinControl,但是自行模仿了TCustomControl的全部行为,一共三种自绘的覆盖方法,比TCustomControl还多一种的更多相关文章

  1. 还在手动给css加前缀?no!几种自动处理css前缀的方法简介

    原文首发于个人博客:还在手动给css加前缀?no!几种自动处理css前缀的方法简介 我们知道在写css的时候由于要兼容不同厂商浏览器,一些比较新的属性需要给它们添加厂商前缀来兼容.移动端还好,基本只要 ...

  2. js中几种实用的跨域方法原理详解(转)

    今天研究js跨域问题的时候发现一篇好博,非常详细地讲解了js几种跨域方法的原理,特分享一下. 原博地址:http://www.cnblogs.com/2050/p/3191744.html 下面正文开 ...

  3. Java中有四种常见的Map实现方法

    在 HTML5 之前我们做图片预览主流做法有两种,第一种是通过 Flash 插件来做预览,第二种是 Ajax 实现的假预览,也就是说选择图片文件后,图片其实已经异步上传到服务器,服务器处理后返回图片路 ...

  4. js中几种实用的跨域方法原理详解

    这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被 ...

  5. [转]js中几种实用的跨域方法原理详解

    转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 // // 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同 ...

  6. js中几种实用的跨域方法原理详解【转】

    源地址:http://www.cnblogs.com/2050/p/3191744.html 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通 ...

  7. GOF提出的23种设计模式是哪些 设计模式有创建形、行为形、结构形三种类别 常用的Javascript中常用设计模式的其中17种 详解设计模式六大原则

    20151218mark 延伸扩展: -设计模式在很多语言PHP.JAVA.C#.C++.JS等都有各自的使用,但原理是相同的,比如JS常用的Javascript设计模式 -详解设计模式六大原则 设计 ...

  8. 再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结

    1. 前言 bxCAN是STM32系列最稳定的IP核之一,无论有哪个新型号出来,这个IP核基本未变,可见这个IP核的设计是相当成熟的.本文所讲述的内容属于这个IP核的一部分,掌握了本文所讲内容,就可以 ...

  9. 五种WordPress防止垃圾评论方法-过滤垃圾评论提高WP运行效率

    WordPress貌似和垃圾评论是一对“孪生兄弟”,无论在国内还是国外的空间主机上搭建的Wordpress博客,无论Wordpress有多少流量多么低的权重,垃圾评论都会自动找上门来,假如有好几天没有 ...

随机推荐

  1. CodeForces 225C Barcode DP

    也是一道dp ,想到了就会觉得很巧妙 矩阵中只有白块和黑块,要求repaint后满足下述条件: 每列一种颜色 根据输入范围x, y 要求条纹宽度在[x, y] 之间 数据范围: n, m, x and ...

  2. HDU2138 随机素数测试 Miller-Rabin算法

    题目描述 Give you a lot of positive integers, just to find out how many prime numbers there are.. In eac ...

  3. HTTPS 中双向认证SSL 协议的具体过程

    HTTPS 中双向认证SSL 协议的具体过程: 这里总结为详细的步骤: ① 浏览器发送一个连接请求给安全服务器.② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器.③ 客户浏览器检查服务器送 ...

  4. 用python解析html

    python中,有三个库可以解析html文本,HTMLParser,sgmllib,htmllib.他们的实现方法不通,但功能差不多.这三个库中 提供解析html的类都是基类,本身并不做具体的工作.他 ...

  5. HDU 4740 模拟题意

    九野的博客,转载请注明出处:http://blog.csdn.net/acmmmm/article/details/11711743 题意:驴和老虎在方格中跑,跑的方式:径直跑,若遇到边界或之前走过的 ...

  6. 一款简单的客户端安卓手机qq源码

    给大家分享一款比较简单的安卓手机qq应用源码,效果非常不错,大家可以借鉴一下,希望大家会喜欢. 1.png  (7.24 KB, 下载次数: 0) 02.png  (68.52 KB, 下载次数: 0 ...

  7. c++实现查询天气预报

    原地址:http://blog.csdn.net/x_iya/article/details/8583015 用到的函数.API等 1.中央气象台API返回的JSON数据(http://m.weath ...

  8. javascript每日一练(八)——事件三:默认行为

    一.阻止默认行为 return false; 自定义右键菜单 <!doctype html> <html> <head> <meta charset=&quo ...

  9. SQL - 将NULL设置为 NOT NULL

    在有些时候,我们在创建表的时候,会忘掉设置某一个列的属性,比如忘了设置为非空,但是默认情况下系统会自动的设置为NULL.这样我们该怎样通过语句设置呢?? 例如有一个表,其中的一个属性为WM CHAR( ...

  10. 严重: Servlet.service() for servlet jsp threw exception java.lang.IllegalStateException: getOutputStream() has already been called

    错误: 严重: Servlet.service() for servlet jsp threw exception java.lang.IllegalStateException: getOutput ...