Рассмотренное выше поведение TComponent позволяет строить просто и без потери строгой типизации, связывать компоненты приложения, а также единообразно вызывать их методы, не требуя, чтобы компоненты были унаследованы от общего предка. Достаточно лишь реализовать в компоненте интерфейс, а в вызывающей программе проверить его наличие.
В качестве примера рассмотрим MDI-приложение, имеющее много различных форм и единую панель инструментов. Предположим, что на этой панели инструментов имеются команды «Сохранить», «Загрузить» и «Очистить», однако каждое из окон реагирует на эти команды по-разному.
Создадим модуль с объявлениями интерфейсов:
unit ToolbarInterface; interface type TCommandType = (ctSave, ctLoad, ctClear); TCommandTypes = set of TCommandType; TSaveType = (stSave, stSaveAS); IToolBarCommands = interface ['{B5266AE1-5E77-11D4-84DD-9153115ABFC3}'] function SupportedCommands: TCommandTypes; function Save(AType: TSaveType): Boolean; procedure Load; procedure Clear; end; implementation end.Интерфейс IToolBarCommands описывает набор методов, которые должны реализовать формы, поддерживающие работу с панелью кнопок. Метод SupportedCommands возвращает список поддерживаемых формой команд.
Создадим три дочерние формы — Form2, Form3 и Form4 — и установим им свойство FormStyle = fsMDIChild.
Form2 умеет выполнять все три команды:
type TForm2 = class(TForm, IToolBarCommands) private function SupportedCommands: TCommandTypes; function Save(AType: TSaveType): Boolean; procedure Load; procedure Clear; end; { TForm2 } procedure TForm2.Clear; begin ShowMessage('TForm2.Clear'); end; procedure TForm2.Load; begin ShowMessage('TForm2.Load'); end; function TForm2.Save(AType: TSaveType): Boolean; begin ShowMessage('TForm2.Save'); Result := TRUE; end; function TForm2.SupportedCommands: TCommandTypes; begin Result := [ctSave, ctLoad, ctClear] end;Form3 умеет выполнять только команду Clear:
type TForm3 = class(TForm, IToolBarCommands) private function SupportedCommands: TCommandTypes; function Save(AType: TSaveType): Boolean; procedure Load; procedure Clear; end; { TForm3 } procedure TForm3.Clear; begin ShowMessage('TForm3.Clear'); end; procedure TForm3.Load; begin // Метод ничего не делает, но должен присутствовать // для корректной реализации интерфейса end; function TForm3.Save(AType: TSaveType): Boolean; begin end; function TForm3.SupportedCommands: TCommandTypes; begin Result := [ctClear] end;И наконец, Form4 вообще не реализует интерфейс IToolBarCommands и не откликается ни на одну команду.
На главной форме приложения поместим ActionList и создадим три компонента TAction. Кроме того, разместим на ней TToolBar и назначим ее кнопкам соответствующие TAction.
type TForm1 = class(TForm) ToolBar1: TToolBar; ImageList1: TImageList; ActionList1: TActionList; acLoad: TAction; acSave: TAction; acClear: TAction; tbSave: TToolButton; tbLoad: TToolButton; tbClear: TToolButton; procedure acLoadExecute(Sender: TObject); procedure ActionList1Update(Action: TBasicAction; var Handled: Boolean); procedure acSaveExecute(Sender: TObject); procedure acClearExecute(Sender: TObject); end;Наиболее интересен метод ActionList1Update, в котором проверяются поддерживаемые активной формой команды и настраивается интерфейс главной формы. Если нет активной дочерней формы либо она не поддерживает интерфейс IToolBarCommands, все команды запрещаются, в противном случае — разрешаются только поддерживаемые формой команды.
procedure TForm1.ActionList1Update(Action: TBasicAction; var Handled: Boolean); var Supported: TCommandTypes; TC: IToolBarCommands; begin if Assigned(ActiveMDIChild) and ActiveMDIChild.GetInterface(IToolBarCommands, TC) then Supported := TC.SupportedCommands else Supported := []; acSave.Enabled := ctSave in Supported; acLoad.Enabled := ctLoad in Supported; acClear.Enabled := ctClear in Supported; end;При активизации команд проверяется наличие активной дочерней формы, у нее запрашивается интерфейс IToolBarCommands и вызывается соответствующий ему метод:
procedure TForm1.acLoadExecute(Sender: TObject); var TC: IToolBarCommands; begin if Assigned(ActiveMDIChild) and ActiveMDIChild.GetInterface(IToolBarCommands, TC) then TC.Load; end; procedure TForm1.acSaveExecute(Sender: TObject); var TC: IToolBarCommands; begin if Assigned(ActiveMDIChild) and ActiveMDIChild.GetInterface(IToolBarCommands, TC) then if not TC.Save(stSaveAS) then ShowMessage(‘Not Saved !!!’); end; procedure TForm1.acClearExecute(Sender: TObject); var TC: IToolBarCommands; begin if Assigned(ActiveMDIChild) and ActiveMDIChild.GetInterface(IToolBarCommands, TC) then TC.Clear; end;Работа программы представлена на рисунке.
Того же эффекта можно добиться и другими методами (например, унаследовав все дочерние формы от единого предка либо обмениваясь с ними сообщениями), однако эти методы имеют ряд существенных недостатков.
Так, при обмене сообщениями мы теряем строгую типизацию и вынуждены передавать параметры через целые числа, а при визуальном наследовании мы привязываем себя к родительскому классу, что не всегда удобно. К тому же можно определить множество интерфейсов и реализовывать в каждой из дочерних форм лишь необходимые, а в случае наследования все формы должны будут реализовывать все общие методы.
![]() |
![]() |