继承自TWinControl的控件不能在设计期间接受子控件,用代码设置子控件却可以(它的自绘是直接改写PaintWindow虚函数,而不是覆盖Paint函数——对TWinControl.WMPaint又有新解了)
2014-08-23 20:31
567 查看
这个控件直接继承自TWinControl,因此不是改写Paint;函数,而是直接改写PaintWindow虚函数,它在VCL框架里被直接调用,直接就把自己画好了(不用走给控件Perform(WM_Paint)的路线了),很有意思。
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
但代码指定子控件则可以,而且还能跟随父控件一起销毁:
---------------------------------------------------------------------------
不使用Paint()的原因也比较清楚(TWinControl根本没有Paint函数):
------------------------------------------------------------------------------------------------
unit MyWinControl; interface uses SysUtils, Classes, Controls, Windows; type TMyWinControl = class(TWinControl) protected procedure PaintWindow(DC: HDC); override; public constructor Create(AOwner: TComponent); override; end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples', [TMyWinControl]); end; constructor TMyWinControl.Create(AOwner: TComponent); begin inherited Create(AOwner); ControlState := ControlState + [csCustomPaint]; // 强行加上了自绘条件,改变了自绘的走向,使用这种方法,控件不需要处理WM_PAINT消息(不需要WMPaint函数和Paint函数) // 必须通知 WMPaint 需要画自己 end; procedure TMyWinControl.PaintWindow(DC: HDC); var Rect: TRect; begin Windows.GetClientRect(Handle, Rect); FillRect(DC, Rect, COLOR_BTNSHADOW + 1); SetBkMode(DC, TRANSPARENT); DrawText(DC, 'Hello, TMyWinControl', -1, Rect, DT_SINGLELINE or DT_VCENTER or DT_CENTER); end; end.
------------------------------------------------------------------------------------------------
但代码指定子控件则可以,而且还能跟随父控件一起销毁:
procedure TForm1.Button1Click(Sender: TObject); begin button2.Parent:=MyWinControl1; end; procedure TForm1.Button3Click(Sender: TObject); begin MyWinControl1.Destroy; end;
---------------------------------------------------------------------------
不使用Paint()的原因也比较清楚(TWinControl根本没有Paint函数):
procedure TWinControl.WMPaint(var Message: TWMPaint); var DC, MemDC: HDC; MemBitmap, OldBitmap: HBITMAP; PS: TPaintStruct; begin if not FDoubleBuffered or (Message.DC <> 0) then begin if not (csCustomPaint in ControlState) and (ControlCount = 0) then inherited // 优先找子类的WM_PAINT消息函数,找不到就调用DefaultHandler函数,这么说,所有TWinControl(非TCustomControl)都处理了WM_PAINT消息? else PaintHandler(Message); // 要求自绘,所以走这里 end else begin DC := GetDC(0); MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom); ReleaseDC(0, DC); MemDC := CreateCompatibleDC(0); OldBitmap := SelectObject(MemDC, MemBitmap); try DC := BeginPaint(Handle, PS); Perform(WM_ERASEBKGND, MemDC, MemDC); Message.DC := MemDC; WMPaint(Message); Message.DC := 0; BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, 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 = 0 then DC := BeginPaint(Handle, PS); try if FControls = nil then PaintWindow(DC) else // 一般情况下,这样执行就结束了 begin SaveIndex := SaveDC(DC); Clip := SimpleRegion; for I := 0 to FControls.Count - 1 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 = 0 then EndPaint(Handle, PS); end; end; procedure TWinControl.PaintWindow(DC: HDC); // 这个函数等于没用(它是virtual函数),必须覆盖 var Message: TMessage; begin Message.Msg := WM_PAINT; Message.WParam := DC; Message.LParam := 0; Message.Result := 0; DefaultHandler(Message); end;
相关文章推荐
- 研究一下TForm.WMPaint过程(也得研究WM_ERASEBKGND)——TForm虽然继承自TWinControl,但是自行模仿了TCustomControl的全部行为,一共三种自绘的覆盖方法,比TCustomControl还多一种
- TGraphicControl(自绘就2步,直接自绘自己,不需要调用VCL框架提供的函数重绘所有子控件,也不需要自己来提供PaintWindow函数让管理框架来调用)与TControl关键属性方法速记(Repaint要求父控件执行详细代码来重绘自己,还是直接要求Invalidate无效后Update刷新父控件,就看透明不透明这个属性,因为计算显示的区域有所不同)
- OnClick事件的Sender参数的前世今生——TWinControl.WinProc优先捕捉到鼠标消息,然后使用IsControlMouseMsg函数进行消息转发给图形子控件(意外发现OnClick是由WM_LBUTTONUP触发的)
- 自绘实现半透明水晶按钮(继承CButton,设置BS_OWNERDRAW风格,覆盖DrawItem函数绘制按钮,把父窗口的背景复制到按钮上,实现视觉上的透明,最后通过AlphaBlend实现半透明)
- 编辑框等控件边框美化(继承CEdit,然后覆盖OnMouseLeave, OnSetFocus, OnPaint函数即可。原来的CEdit虽然代码不可见,但它也是有句柄的,照样随便画)
- 在xib上拖进一个scrollView,设置contentsize之后可以滑动,再往其上拖上控件之后就不能滑动,控件用代码写的就可以
- ActiveX 控件“Microsoft Chart Control 6.0(sp4)(OLEDB)"不能例示,因为它需要一个设计时间许可
- 继承于抽象类的控件不能使用设计器
- 使用SendMessage方法对窗体上的控件进行截图,该方法的思想就是把控件的句柄拿到,对控件发送WM_PAINT消息,并且把希望得到图形对象的句柄当作wParam参数传过去,这样就可以在图形对象得到想要得图形。
- cocos studio设计界面,在代码中寻找按钮,设置监听函数等
- [ASP.NET]为什么CustomValidator验证控件在验证DropdownList的时候不能设置ControlToValidate属性
- 基本上,把switch,用设计模式代替,肯定是bug和过度设计。想想,本来修改一个文件几行代码可以解决的问题,变成修改3-6个类才能实现一样的功能。不是傻是什么?
- C#中控件Control的Paint事件和OnPaint虚函数的区别
- TWinControl与TControl的覆盖函数(TWinControl对TControl的10个消息覆盖函数,17个覆盖函数,私有虚函数仍可多态)
- 一句话改变TWinControl控件的left坐标的前世今生(入口函数是SetBounds,然后调用SetWindowPos起作用,并发消息更新Delphi的left属性值)
- C#:Control控件里的ComboBox在设计视图下焦点可以进入的解决办法
- Windows 8的无线设置后,竟不能直接更改,目前知道可以通过命令行解决
- ActiveX 控件“Microsoft Chart Control 6.0(sp4)(OLEDB)"不能例示,因为它需要一个设计时间许可
- TWinControl的刷新过程(5个非虚函数,4个覆盖函数,1个消息函数,默认没有双缓冲,注意区分是TCustomControl还是Windows原生封装控件,执行流程不一样)
- C# Winform 一个可以用鼠标改变控件位置和大小的类,直接调用即可.....(代码收藏)