超類化源碼:html
procedure TWinControl.CreateSubClass(var Params: TCreateParams; ControlClassName: PChar); const {CS_OWNDC標誌,屬於此窗口類的窗口實例都有本身的DC(稱爲私有DC) } {CS_CLASSDC標誌,全部屬於該類的窗口實例共享相同的DC(稱爲類DC).類DC有一些私有DC的優勢,而更加節約內存} {CS_PARENTDC標誌,屬於這個類的窗口都使用它的父窗口的句柄。和CS_CLASSDC類似的是,多個窗口共享一個DC,不一樣的是,這多個窗口(雖然有父子關係而且共享DC)並不要求都屬於同一個窗口類} {CS_GLOBALCLASS標誌,是惟一一個針對類自己起做用而不是對單個窗口起做用的標誌。} CS_OFF = CS_OWNDC or CS_CLASSDC or CS_PARENTDC or CS_GLOBALCLASS; {CS_HREDRAW和CS_VREDRAW標誌表示當窗口的水平尺寸(寬度)改變的時候,重畫整個窗口。按鈕和滾動條都有這兩種風格。} CS_ON = CS_VREDRAW or CS_HREDRAW; var SaveInstance: THandle; begin // 說是子類化,實際上是超類化 // http://www.cnblogs.com/findumars/p/4121704.html Delphi對Button的超類化 // http://www.cnblogs.com/findumars/p/4680601.html 子類化是窗口實例級別的,超類化是在窗口類(WNDCLASS)級別的 // http://www.cnblogs.com/sfqh/p/3384457.html 探索Win32系統之窗口類 // important 兩個參數:Windows類的風格,類名(Windows的內置類) // Creates a windowed control derived from an existing Windows window class. // CreateSubClass allows VCL controls to create registered Windows controls. // 問題:不明白,哪裏建立新類了。回答:根據已有的Windows類,建立Windows控件,注意是控件,不是類。 // 此單元沒有調用此函數,但TButton,TEdit,TCombobox 等等都調用了它。 if ControlClassName <> nil then // 若是類名不爲空 with Params do // 它是可變參數,結構體Params.WindowClass begin // 記錄當前Windows類的句柄實例(實際上是整個EXE模塊的句柄) SaveInstance := WindowClass.hInstance; // API取得相關信息(第三個參數)。 失敗返回0 // 只有三次執行都失敗(2個句柄,一個類名),條件才成立。也就是這個ControlClassName新類尚未在內存中註冊過。 if not GetClassInfo(HInstance, ControlClassName, WindowClass) and // API 取得信息填充,第一個參數是Application.Instance(全局變量),第二個參數是類名,第三個參數等待填充的結構,即Params.WindowClass not GetClassInfo(0, ControlClassName, WindowClass) and // not GetClassInfo(MainInstance, ControlClassName, WindowClass) // MainInstance就這一處,標識EXE文件的Instance(系統級全局變量) then // 根據句柄和名稱獲得WindowClass的全部信息,注意,有可能覆蓋了原先的hInstance,因此要事先記錄,過後賦值 GetClassInfo(WindowClass.hInstance, ControlClassName, WindowClass); // API,第三個參數是Out // 一旦發現類名註冊過了,就什麼都不用作 // 爲了保險起見,除了EXE模塊句柄不得不從新賦值之外,其它一切舊有記錄信息從Windows內核中直接取出。連類名都有可能被改變。 // fixme TButton調用了它,應該跟蹤一下。 WindowClass.hInstance := SaveInstance; // 改變風格標記,不但願自繪,而且窗口大小或位置改變後,就重繪整個窗口 // 注意,TEdit等等都是直接繼承自TWinControl,沒有自繪句柄。 WindowClass.style := WindowClass.style and not CS_OFF or CS_ON; // fixme 拋棄一切DC,準備使用Delphi體系的Canvas進行自繪。 // 問題:執行之後,類名究竟是TButton仍是Button? // important 取到的信息,都經過Params.WindowClass傳出去 end; end;
8種Windows基礎控件:ide
"G:\Vcl\StdCtrls.pas"(1898,3): CreateSubClass(Params, 'EDIT'); "G:\Vcl\StdCtrls.pas"(2986,3): CreateSubClass(Params, 'COMBOBOX'); "G:\Vcl\StdCtrls.pas"(3512,3): CreateSubClass(Params, 'BUTTON'); // TButton "G:\Vcl\StdCtrls.pas"(3658,3): CreateSubClass(Params, 'BUTTON'); // TCustomCheckBox "G:\Vcl\StdCtrls.pas"(3783,3): CreateSubClass(Params, 'BUTTON'); // TRadioButton "G:\Vcl\StdCtrls.pas"(4369,3): CreateSubClass(Params, 'LISTBOX'); "G:\Vcl\StdCtrls.pas"(4783,3): CreateSubClass(Params, 'SCROLLBAR'); "G:\Vcl\StdCtrls.pas"(4984,3): CreateSubClass(Params, 'STATIC');
17種Windows複雜控件:函數
"G:\Vcl\ComCtrls.pas"(11434,3): CreateSubClass(Params, 'RICHEDIT'); "G:\Vcl\ComCtrls.pas"(4263,3): CreateSubClass(Params, WC_TABCONTROL); "G:\Vcl\ComCtrls.pas"(5604,3): CreateSubClass(Params, STATUSCLASSNAME); "G:\Vcl\ComCtrls.pas"(6268,3): CreateSubClass(Params, WC_HEADER); "G:\Vcl\ComCtrls.pas"(8410,3): CreateSubClass(Params, WC_TREEVIEW); "G:\Vcl\ComCtrls.pas"(10039,3): CreateSubClass(Params, TRACKBAR_CLASS); "G:\Vcl\ComCtrls.pas"(10376,3): CreateSubClass(Params, PROGRESS_CLASS); "G:\Vcl\ComCtrls.pas"(11856,3): CreateSubClass(Params, UPDOWN_CLASS); "G:\Vcl\ComCtrls.pas"(12181,3): CreateSubClass(Params, HOTKEYCLASS); "G:\Vcl\ComCtrls.pas"(13593,3): CreateSubClass(Params, WC_LISTVIEW); "G:\Vcl\ComCtrls.pas"(15775,3): CreateSubClass(Params, ANIMATE_CLASS); "G:\Vcl\ComCtrls.pas"(16837,3): CreateSubClass(Params, TOOLBARCLASSNAME); "G:\Vcl\ComCtrls.pas"(19840,3): CreateSubClass(Params, REBARCLASSNAME); "G:\Vcl\ComCtrls.pas"(21313,3): CreateSubClass(Params, MONTHCAL_CLASS); "G:\Vcl\ComCtrls.pas"(21421,3): CreateSubClass(Params, DATETIMEPICK_CLASS); "G:\Vcl\ComCtrls.pas"(21691,3): CreateSubClass(Params, WC_PAGESCROLLER); "G:\Vcl\ComCtrls.pas"(22175,3): CreateSubClass(Params, WC_COMBOBOXEX);
本着任何技術都要爛熟於心的精神,把8種基礎控件的代碼貼上來,混個臉熟,之後再加上註釋:spa
procedure TCustomEdit.CreateParams(var Params: TCreateParams); const Passwords: array[Boolean] of DWORD = (0, ES_PASSWORD); ReadOnlys: array[Boolean] of DWORD = (0, ES_READONLY); CharCases: array[TEditCharCase] of DWORD = (0, ES_UPPERCASE, ES_LOWERCASE); HideSelections: array[Boolean] of DWORD = (ES_NOHIDESEL, 0); OEMConverts: array[Boolean] of DWORD = (0, ES_OEMCONVERT); begin inherited CreateParams(Params); CreateSubClass(Params, 'EDIT'); with Params do begin Style := Style or (ES_AUTOHSCROLL or ES_AUTOVSCROLL) or BorderStyles[FBorderStyle] or Passwords[FPasswordChar <> #0] or ReadOnlys[FReadOnly] or CharCases[FCharCase] or HideSelections[FHideSelection] or OEMConverts[FOEMConvert]; if NewStyleControls and Ctl3D and (FBorderStyle = bsSingle) then begin Style := Style and not WS_BORDER; ExStyle := ExStyle or WS_EX_CLIENTEDGE; end; end; end; procedure TCustomComboBox.CreateParams(var Params: TCreateParams); const ComboBoxStyles: array[TComboBoxStyle] of DWORD = ( CBS_DROPDOWN, CBS_SIMPLE, CBS_DROPDOWNLIST, CBS_DROPDOWNLIST or CBS_OWNERDRAWFIXED, CBS_DROPDOWNLIST or CBS_OWNERDRAWVARIABLE); CharCases: array[TEditCharCase] of DWORD = (0, CBS_UPPERCASE, CBS_LOWERCASE); Sorts: array[Boolean] of DWORD = (0, CBS_SORT); begin inherited CreateParams(Params); CreateSubClass(Params, 'COMBOBOX'); with Params do Style := Style or (WS_VSCROLL or CBS_HASSTRINGS or CBS_AUTOHSCROLL) or ComboBoxStyles[FStyle] or Sorts[FSorted] or CharCases[FCharCase]; end; procedure TButton.CreateParams(var Params: TCreateParams); const ButtonStyles: array[Boolean] of DWORD = (BS_PUSHBUTTON, BS_DEFPUSHBUTTON); begin inherited CreateParams(Params); CreateSubClass(Params, 'BUTTON'); Params.Style := Params.Style or ButtonStyles[FDefault]; end; procedure TCustomCheckBox.CreateParams(var Params: TCreateParams); const Alignments: array[Boolean, TLeftRight] of DWORD = ((BS_LEFTTEXT, 0), (0, BS_LEFTTEXT)); begin inherited CreateParams(Params); CreateSubClass(Params, 'BUTTON'); with Params do begin Style := Style or BS_3STATE or Alignments[UseRightToLeftAlignment, FAlignment]; WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW); end; end; procedure TRadioButton.CreateParams(var Params: TCreateParams); const Alignments: array[Boolean, TLeftRight] of DWORD = ((BS_LEFTTEXT, 0), (0, BS_LEFTTEXT)); begin inherited CreateParams(Params); CreateSubClass(Params, 'BUTTON'); with Params do Style := Style or BS_RADIOBUTTON or Alignments[UseRightToLeftAlignment, FAlignment]; end; procedure TCustomListBox.CreateParams(var Params: TCreateParams); type PSelects = ^TSelects; TSelects = array[Boolean] of DWORD; const Styles: array[TListBoxStyle] of DWORD = (0, LBS_OWNERDRAWFIXED, LBS_OWNERDRAWVARIABLE, LBS_OWNERDRAWFIXED, LBS_OWNERDRAWFIXED); Sorteds: array[Boolean] of DWORD = (0, LBS_SORT); MultiSelects: array[Boolean] of DWORD = (0, LBS_MULTIPLESEL); ExtendSelects: array[Boolean] of DWORD = (0, LBS_EXTENDEDSEL); IntegralHeights: array[Boolean] of DWORD = (LBS_NOINTEGRALHEIGHT, 0); MultiColumns: array[Boolean] of DWORD = (0, LBS_MULTICOLUMN); TabStops: array[Boolean] of DWORD = (0, LBS_USETABSTOPS); CSHREDRAW: array[Boolean] of DWORD = (CS_HREDRAW, 0); Data: array[Boolean] of DWORD = (LBS_HASSTRINGS, LBS_NODATA); var Selects: PSelects; begin inherited CreateParams(Params); CreateSubClass(Params, 'LISTBOX'); with Params do begin Selects := @MultiSelects; if FExtendedSelect then Selects := @ExtendSelects; Style := Style or (WS_HSCROLL or WS_VSCROLL or Data[Self.Style in [lbVirtual, lbVirtualOwnerDraw]] or LBS_NOTIFY) or Styles[FStyle] or Sorteds[FSorted] or Selects^[FMultiSelect] or IntegralHeights[FIntegralHeight] or MultiColumns[FColumns <> 0] or BorderStyles[FBorderStyle] or TabStops[FTabWidth <> 0]; if NewStyleControls and Ctl3D and (FBorderStyle = bsSingle) then begin Style := Style and not WS_BORDER; ExStyle := ExStyle or WS_EX_CLIENTEDGE; end; WindowClass.style := WindowClass.style and not (CSHREDRAW[UseRightToLeftAlignment] or CS_VREDRAW); end; end; procedure TScrollBar.CreateParams(var Params: TCreateParams); const Kinds: array[TScrollBarKind] of DWORD = (SBS_HORZ, SBS_VERT); begin inherited CreateParams(Params); CreateSubClass(Params, 'SCROLLBAR'); Params.Style := Params.Style or Kinds[FKind]; if FKind = sbVertical then if not UseRightToLeftAlignment then Params.Style := Params.Style or SBS_RIGHTALIGN else Params.Style := Params.Style or SBS_LEFTALIGN; if NotRightToLeft then FRTLFactor := 1 else FRTLFactor := -1; end; procedure TCustomStaticText.CreateParams(var Params: TCreateParams); const Alignments: array[Boolean, TAlignment] of DWORD = ((SS_LEFT, SS_RIGHT, SS_CENTER), (SS_RIGHT, SS_LEFT, SS_CENTER)); Borders: array[TStaticBorderStyle] of DWORD = (0, WS_BORDER, SS_SUNKEN); begin inherited CreateParams(Params); CreateSubClass(Params, 'STATIC'); with Params do begin Style := Style or SS_NOTIFY or Alignments[UseRightToLeftAlignment, FAlignment] or Borders[FBorderStyle]; if not FShowAccelChar then Style := Style or SS_NOPREFIX; WindowClass.style := WindowClass.style and not CS_VREDRAW; end; end;
再看看RichEdit的封裝代碼:code
procedure TCustomRichEdit.CreateParams(var Params: TCreateParams); const RichEditModuleName = 'RICHED32.DLL'; HideScrollBars: array[Boolean] of DWORD = (ES_DISABLENOSCROLL, 0); HideSelections: array[Boolean] of DWORD = (ES_NOHIDESEL, 0); begin if FRichEditModule = 0 then begin FRichEditModule := LoadLibrary(RichEditModuleName); if FRichEditModule <= HINSTANCE_ERROR then FRichEditModule := 0; end; inherited CreateParams(Params); CreateSubClass(Params, 'RICHEDIT'); with Params do begin Style := Style or HideScrollBars[FHideScrollBars] or HideSelections[HideSelection]; WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW); end; end;