Переопределение визуальных компонентов в DelphiИсточник: delphi2010 Александр Божко
Недавно у меня возникла проблема с расширением функционала одного довольно известного набора сторонних компонентов. Решение нашлось тут же, на сайте производителя и представляло собой довольно интересную синтаксическую конструкцию. Собственно, никакого откровения там не было, скорее меня удивило то, насколько эффективно использован описываемый ниже прием.Попытаюсь объяснить все "на пальцах". Допустим, у нас имеется приложение, в котором жестко изменен цвет всех форм. Однако все находящиеся на форме экземпляры класса TButton будут иметь цвет clBtnFace. Изменить цвет кнопки - не проблема. Здесь я пропущу реализацию класса, приведу только его описание. unit ColorButton; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls; type TDrawButtonEvent = procedure(Control: TWinControl; Rect: TRect; State: TOwnerDrawState) of object; TColorButton = class(TButton) private FCanvas: TCanvas; IsFocused: Boolean; FOnDrawButton: TDrawButtonEvent; protected procedure CreateParams(var Params: TCreateParams); override; procedure SetButtonStyle(ADefault: Boolean); override; procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED; procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED; procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM; procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM; procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK; procedure DrawButton(Rect: TRect; State: UINT); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property Canvas: TCanvas read FCanvas; published property OnDrawButton: TDrawButtonEvent read FOnDrawButton write FOnDrawButton; property Color; end; procedure Register; implementation {skiped} End. Соответственно, процедура Register отвечает за регистрацию класса в палитре компонентов. procedure Register; begin RegisterComponents('Samples', [TColorButton]); end; Не буду здесь описывать механику сборки компонента из исходного кода. Более интересный вопрос заключается в том, как максимально эффективно преобразовать наше приложение к требуемому виду. Иными словами, заменить все компоненты TButton на компоненты TcolorButton. unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ColorButton; Type // Перекрываем класс TButton = class(ColorButton.TColorButton) end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; Implementation {skiped} ColorButton - имя модуля, в котором определен класс TColorButton. Объявить тип TButton непосредственно в модуле ColorButton. unit ColorButton; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls; type TDrawButtonEvent = procedure(Control: TWinControl; Rect: TRect; State: TOwnerDrawState) of object; TColorButton = class(TButton) private FCanvas: TCanvas; IsFocused: Boolean; FOnDrawButton: TDrawButtonEvent; protected procedure CreateParams(var Params: TCreateParams); override; procedure SetButtonStyle(ADefault: Boolean); override; procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED; procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED; procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM; procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM; procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK; procedure DrawButton(Rect: TRect; State: UINT); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property Canvas: TCanvas read FCanvas; published property OnDrawButton: TDrawButtonEvent read FOnDrawButton write FOnDrawButton; property Color; end; // Объявить тип TButton непосредственно в модуле ColorButton. TButton = class(ColorButton.TColorButton) end; Тогда, для достижения результата нам следует просто подключить модуль ColorButton в интерфейс формы. unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ColorButton; // ' подключили модуль ColorButton type TForm1 = class(TForm) {skiped} Тут следует обратить внимание на один нюанс. unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ColorButton, StdCtrls; // ' меняем местами модули и возвращаем старое поведение компонента. type TForm1 = class(TForm) {skiped} В ряде случае такой подход можно использовать довольно эффективно. Хотя бы только потому, вписав одну строку в код, мы автоматически меняем поведение всех кнопок данного класса на форме. Конечно, все это справедливо только в том случае, если измененный (перекрывающий) компонент является потомком изменяемого компонента. |