TCustomControl = class(TWinControl) private FCanvas: TCanvas; procedure WMPaint(var Message: TWMPaint); message WM_PAINT; protected procedure Paint; virtual; procedure PaintWindow(DC: HDC); override; property Canvas: TCanvas read FCanvas; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; end; constructor TCustomControl.Create(AOwner: TComponent); begin inherited Create(AOwner); FCanvas := TControlCanvas.Create; TControlCanvas(FCanvas).Control := Self; end; destructor TCustomControl.Destroy; begin FCanvas.Free; inherited Destroy; end; procedure TCustomControl.WMPaint(var Message: TWMPaint); begin Include(FControlState, csCustomPaint); inherited; Exclude(FControlState, csCustomPaint); end; procedure TCustomControl.PaintWindow(DC: HDC); begin FCanvas.Lock; try FCanvas.Handle := DC; try TControlCanvas(FCanvas).UpdateTextFlags; Paint; finally FCanvas.Handle := 0; end; finally FCanvas.Unlock; end; end; procedure TCustomControl.Paint; begin end;
TWinControl用来处理鼠标和实际重绘,而处理鼠标全部在TControl里。此外还申请句柄,处理自己绘图,与父类的交互。虽然TControl也有绘图,但最后还是要让父类TWinControl来画。
TWinControl = class(TControl) constructor Create(AOwner: TComponent); override; constructor CreateParented(ParentWindow: HWnd); class function CreateParentedControl(ParentWindow: HWND): TWinControl; destructor Destroy; override; procedure Broadcast(var Message); function GetHandle: HWND; procedure SetParentWindow(Value: HWND); function GetControlCount: Integer; function GetControl(Index: Integer): TControl; procedure Insert(AControl: TControl); procedure SetZOrderPosition(Position: Integer); function HandleAllocated: Boolean; procedure HandleNeeded; procedure InsertControl(AControl: TControl); procedure CreateHandle; virtual; procedure CreateParams(var Params: TCreateParams); virtual; procedure CreateWindowHandle(const Params: TCreateParams); virtual; procedure CreateWnd; virtual; procedure RecreateWnd; procedure DestroyHandle; virtual; procedure DestroyWindowHandle; virtual; procedure DestroyWnd; virtual; function GetDeviceContext(var WindowHandle: HWND): HDC; overload; override; function GetParentHandle: HWND; function GetTopParentHandle: HWnd; procedure SetParent(AParent: TWinControl); override; procedure SetParentBackground(Value: Boolean); virtual; procedure SetParentDoubleBuffered(Value: Boolean); virtual; procedure SetZOrder(TopMost: Boolean); override; procedure RemoveControl(AControl: TControl);procedure UpdateControlState;
procedure CreateSubClass(var Params: TCreateParams; ControlClassName: PChar); procedure WndProc(var Message: TMessage); override; property DefWndProc: Pointer read FDefWndProc write FDefWndProc; property WindowHandle: HWnd read FHandle write FHandle; procedure DefaultHandler(var Message); override; procedure MainWndProc(var Message: TMessage); function PreProcessMessage(var Msg: TMsg): Boolean; dynamic; procedure Repaint; override; // 就两句:调用 类函数Invalidate; 和 类函数Update; // 让程序员有机会总揽全局,既使控件失效,又立即重绘,因为一般情况下只需要让控件失效即可,然后系统空闲时发信息让控件重绘,程序员不需要管理和调用后者。
procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure NotifyControls(Msg: Word); procedure PaintControls(DC: HDC; First: TControl); // 给FControls发Perform(WM_PAINT, DC, 0); 把图像子控件全部重绘了一遍。对所有的FWinControls 调用WinAPI FrameRect 用指定的画刷为指定的矩形画边框 procedure PaintHandler(var Message: TWMPaint); // 在不双缓冲的情况下,用它立即重绘(一般情况下会执行这里),并调用PaintControls,管理功能。
procedure PaintWindow(DC: HDC); virtual; // 声明一个新的 WM_PAINT消息,并发送到DefaultHandler里去,一般情况下被覆盖,所以用不到它。procedure UpdateShowing; // 只处理是否Visible procedure Invalidate; override; // 内容就一句 Perform(CM_INVALIDATE, 0, 0); 另外观察一下TControl.Invalidate;函数 也是就一句:InvalidateControl(Visible, csOpaque in ControlStyle);
procedure PaintTo(Canvas: TCanvas; X, Y: Integer); overload; procedure Update; override; // 调用WinAPI函数 if HandleAllocated then UpdateWindow(WindowHandle); // 会产生WM_PAINT消息。让程序员有机会强行自绘控件。 总结:自绘过程一共有四个函数实际干活,最主要是WMPaint,然后把消息转交给PaintHandler,然后分别绘制自己PaintWindow和绘制子控件PaintControls。
备注:重绘其实就两步,第一步让控件失效,第二步系统给Win控件发送WM_PAINT真正重绘,具体重绘过程由VCL控制,包含了自动重绘图形子控件和调用程序员的事件
procedure AssignTo(Dest: TPersistent); override; function GetClientRect: TRect; override; function IsDoubleBufferedStored: Boolean; procedure SetDoubleBuffered(Value: Boolean); procedure DoEnter; dynamic; procedure DoExit; dynamic; function DoKeyDown(var Message: TWMKey): Boolean; function DoKeyPress(var Message: TWMKey): Boolean; function DoKeyUp(var Message: TWMKey): Boolean; procedure WMInputLangChange(var Message: TMessage); message WM_INPUTLANGCHANGE; // 输入法改变 procedure WMPaint(var Message: TWMPaint); message WM_PAINT; // 这里只是收到消息,收到以后怎么做不一定要放在这里。实际上:如果不双缓冲就调用 PaintHandler,否则立刻重绘(并且还考虑Windows主题的影响)。 procedure WMCommand(var Message: TWMCommand); message WM_COMMAND; // 菜单、工具栏命令 procedure WMNotify(var Message: TWMNotify); message WM_NOTIFY; // 通知消息 procedure WMSysColorChange(var Message: TWMSysColorChange); message WM_SYSCOLORCHANGE; // 系统颜色改变 procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL; procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL; procedure WMCompareItem(var Message: TWMCompareItem); message WM_COMPAREITEM; procedure WMDeleteItem(var Message: TWMDeleteItem); message WM_DELETEITEM; procedure WMDrawItem(var Message: TWMDrawItem); message WM_DRAWITEM; procedure WMMeasureItem(var Message: TWMMeasureItem); message WM_MEASUREITEM; procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND; // 画背景色,间接的发送 WM_ERASEBKGND消息,不实际干活 procedure WMWindowPosChanged(var Message: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED; // 位置改变 procedure WMWindowPosChanging(var Message: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING; procedure WMSize(var Message: TWMSize); message WM_SIZE; procedure WMMove(var Message: TWMMove); message WM_MOVE; procedure WMSetCursor(var Message: TWMSetCursor); message WM_SETCURSOR; // 设置光标 procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN; // 按键方法 procedure WMSysKeyDown(var Message: TWMSysKeyDown); message WM_SYSKEYDOWN; procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP; procedure WMSysKeyUp(var Message: TWMSysKeyUp); message WM_SYSKEYUP; // 系统按键 procedure WMChar(var Message: TWMChar); message WM_CHAR; procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND; // 系统命令 procedure WMCharToItem(var Message: TWMCharToItem); message WM_CHARTOITEM; procedure WMParentNotify(var Message: TWMParentNotify); message WM_PARENTNOTIFY; // 父控件通知 procedure WMVKeyToItem(var Message: TWMVKeyToItem); message WM_VKEYTOITEM; procedure WMDestroy(var Message: TWMDestroy); message WM_DESTROY; // 销毁 procedure WMMouseActivate(var Message: TWMMouseActivate); message WM_MOUSEACTIVATE; procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE; // 非客户区重新计算 procedure WMNCDestroy(var Message: TWMNCDestroy); message WM_NCDESTROY; // 非客户区销毁 procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST; procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT; // 非客户区绘图 procedure WMQueryNewPalette(var Message: TMessage); message WM_QUERYNEWPALETTE; procedure WMPaletteChanged(var Message: TMessage); message WM_PALETTECHANGED; procedure WMWinIniChange(var Message: TMessage); message WM_WININICHANGE; procedure WMFontChange(var Message: TMessage); message WM_FONTCHANGE; // 字体被改变 procedure WMTimeChange(var Message: TMessage); message WM_TIMECHANGE; // 时间被改变 procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS; procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS; procedure WMIMEStartComp(var Message: TMessage); message WM_IME_STARTCOMPOSITION; procedure WMIMEEndComp(var Message: TMessage); message WM_IME_ENDCOMPOSITION; procedure WMContextMenu(var Message: TWMContextMenu); message WM_CONTEXTMENU; procedure WMGesture(var Message: TMessage); message WM_GESTURE; procedure WMGestureNotify(var Message: TWMGestureNotify); message WM_GESTURENOTIFY; procedure WMTabletQuerySystemGestureStatus(var Message: TMessage); message WM_TABLET_QUERYSYSTEMGESTURESTATUS; procedure WMPrintClient(var Message: TWMPrintClient); message WM_PRINTCLIENT;
procedure CMInputLangChange(var Message: TMessage); message CM_INPUTLANGCHANGE; procedure CMChanged(var Message: TCMChanged); message CM_CHANGED; procedure CMChildKey(var Message: TCMChildKey); message CM_CHILDKEY; procedure CMDialogKey(var Message: TCMDialogKey); message CM_DIALOGKEY; procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR; procedure CMVisibleChanged(var Message: TMessage); message CM_VISIBLECHANGED; procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED; procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED; procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED; procedure CMBorderChanged(var Message: TMessage); message CM_BORDERCHANGED; procedure CMCursorChanged(var Message: TMessage); message CM_CURSORCHANGED; procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED; procedure CMParentCtl3DChanged(var Message: TMessage); message CM_PARENTCTL3DCHANGED; procedure CMParentDoubleBufferedChanged(var Message: TMessage); message CM_PARENTDOUBLEBUFFEREDCHANGED; procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED; procedure CMShowHintChanged(var Message: TMessage); message CM_SHOWHINTCHANGED; procedure CMEnter(var Message: TCMEnter); message CM_ENTER; procedure CMExit(var Message: TCMExit); message CM_EXIT; procedure CMDesignHitTest(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST; procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE; procedure CMSysFontChanged(var Message: TMessage); message CM_SYSFONTCHANGED; procedure CMWinIniChange(var Message: TWMWinIniChange); message CM_WININICHANGE; procedure CMFontChange(var Message: TMessage); message CM_FONTCHANGE; procedure CMTimeChange(var Message: TMessage); message CM_TIMECHANGE; procedure CMDrag(var Message: TCMDrag); message CM_DRAG;
procedure CNKeyDown(var Message: TWMKeyDown); message CN_KEYDOWN; procedure CNKeyUp(var Message: TWMKeyUp); message CN_KEYUP; procedure CNChar(var Message: TWMChar); message CN_CHAR; procedure CNSysKeyDown(var Message: TWMKeyDown); message CN_SYSKEYDOWN; procedure CNSysChar(var Message: TWMChar); message CN_SYSCHAR;
procedure CMRecreateWnd(var Message: TMessage); message CM_RECREATEWND; procedure CMInvalidate(var Message: TMessage); message CM_INVALIDATE; procedure CMBiDiModeChanged(var Message: TMessage); message CM_BIDIMODECHANGED; procedure CMDoubleBufferedChanged(var Message: TMessage); message CM_DOUBLEBUFFEREDCHANGED; procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED; procedure CMTabletOptionsChanged(var Message: TMessage); message CM_TABLETOPTIONSCHANGED; procedure CMFocusChanged(var Message: TCMFocusChanged); message CM_FOCUSCHANGED; procedure CMControlListChange(var Message: TMessage); message CM_CONTROLLISTCHANGE; procedure CMControlListChanging(var Message: TMessage); message CM_CONTROLLISTCHANGING; procedure CMDockClient(var Message: TCMDockClient); message CM_DOCKCLIENT; procedure CMUnDockClient(var Message: TCMUnDockClient); message CM_UNDOCKCLIENT; procedure CMFloat(var Message: TCMFloat); message CM_FLOAT; property Brush: TBrush read FBrush; property Controls[Index: Integer]: TControl read GetControl; property ControlCount: Integer read GetControlCount; property Handle: HWND read GetHandle; property ParentDoubleBuffered: Boolean read FParentDoubleBuffered write SetParentDoubleBuffered default True; property ParentWindow: HWND read FParentWindow write SetParentWindow; property Showing: Boolean read FShowing;
TWinControl的属性:
FDefWndProc: Pointer;
FHandle: HWnd;
FObjectInstance: Pointer;
FParentWindow: HWND;
FBrush: TBrush;
FControls: TList;
FWinControls: TList;
FAlignControlList: TList;
FParentDoubleBuffered: Boolean;
FDoubleBuffered: Boolean;
FDesignSize: TPoint;
FMouseControl: TControl;
FTouchControl: TControl;
FOnEnter: TNotifyEvent;
FOnExit: TNotifyEvent;
FOnKeyDown: TKeyEvent;
FOnKeyUp: TKeyEvent;
FOnKeyPress: TKeyPressEvent;
再来窗口参数:
TCreateParams = record Caption: PChar; Style: DWORD; ExStyle: DWORD; X, Y: Integer; Width, Height: Integer; WndParent: HWnd; Param: Pointer; WindowClass: TWndClass; WinClassName: array[0..63] of Char; end;