Невизуальное наследование форм в Delphi

Создание класса.
Создадим базовый класс, наследник TForm, от которого в дальнейшем будем наследовать все наши формы. Для этого в новый или существующий пакет, добавим компонент TBaseForm.

Рис. 1. Создание нового компонента TBaseForm


Для Delphi 6 в пункте Requires нашего пакета добавляем "Borland Designer IDE Package" (по умолчанию находится в C:\Program_Files\Borland\Delphi6\Lib\designide.dcp). Для Delphi 5 все для нас необходимое содержится в "Borland Visual Component Library" (он включается в пакет автоматически). В раздел uses для Delphi 5 добавляем DsgnIntf, а для Delphi 6 DesignIntf и DesignEditors. Изменим процедуру Register только что созданного компонента.

    procedure Register;
    begin
      RegisterCustomModule(TBaseForm, TCustomModule);
    end;
Листинг 1. Изменение процедуры Register.
Компилируем наш пакет - класс TBaseForm готов к использованию.

Использование класса.
Для того чтобы какая-либо форма проекта была наследником класса TBaseForm, для этого в раздел uses формы нужно включить юнит BaseForm, а объявление класса новой формы class(TForm) переделать на class(TBaseForm). Затем форму нужно закрыть и снова открыть. После этого наша форма станет полноценным наследником класса TBaseForm.

Пример использования.
Пусть перед нами стоит задача: все формы проекта должны содержать информацию о версии формы (что необходимо, когда один и тот же проект подгоняется под разных заказчиков) и отображать информацию о форме каким-либо способом.

Для реализации этих целей изменим класс TBaseForm как это показано в листинге 2. Здесь добавлены соответствующие переменные и свойства для хранения информации о версии формы и создан механизм показа информации о форме посредством системного меню окна - добавлен новый пункт системного меню "О форме".

    /////////////////////////////////////////////////////////////
    //        описание класса базовой формы
    //        Рощупкин А.В. 2003 г.
    /////////////////////////////////////////////////////////////
    unit BaseForm;

    interface

    uses
      Windows, Messages, SysUtils, Classes, Controls, Forms,
      //DsgnIntf;                //при использовании Delphi5
      DesignIntf, DesignEditors; //при использовании Delphi6

    const
      ItmID = $00a0; //идентификатор нового пункта системного меню

    type
      TBaseForm = class(TForm)
      private
      FVersion: integer;          //версия формы
      FMnuItmAbout: MENUITEMINFO; //структура нового пункта меню
      FOldWndProc : Pointer;      //указатель на старую функцию обработки
                    //сообщений окна (формы)
      FNewWndProc : Pointer;      //указатель на новую функцию обработки
                    //сообщений окна (формы)

      // возвращает информацию о форме в виде строки
      function GetAboutString: string;

      protected
      public
      //конструктор
      constructor Create(AOwner: TComponent); override;
      //деструктор :)
      destructor  Destroy; override;
      //возвращает указатель на старую функцию обработки сообщений окна (формы)
      function    GetOldWindowProc: Pointer;
      //показывает информацию о форме
      procedure   ShowFormInfo;

      published
      property Version: integer read FVersion write FVersion default 1;

      end;

    //новую функцию обработки сообщений окна (формы)
    function  NewWndProc(wnd: HWND; Msg: UINT; wPrm: WPARAM; lPrm: LPARAM): LRESULT stdcall;
    //процедура регистрации класса
    procedure Register;

    implementation

    constructor TBaseForm.Create(AOwner: TComponent);
    begin
       inherited;

       //у наших окон всегда должно быть системное меню!!!
       if not ( biSystemMenu in Self.BorderIcons ) then
        Self.BorderIcons := Self.BorderIcons + [biSystemMenu];

       //создаем новый пункт систменого меню, по выборе которого
       //будет показана информация о форме
       FMnuItmAbout.cbSize := 44;
       FMnuItmAbout.fMask := MIIM_DATA or MIIM_ID or MIIM_STATE or MIIM_TYPE;
       FMnuItmAbout.fType := MFT_STRING;
       FMnuItmAbout.fState := MFS_ENABLED;
       FMnuItmAbout.wID := ItmID;
       FMnuItmAbout.hSubMenu := 0;
       FMnuItmAbout.hbmpChecked := 0;
       FMnuItmAbout.hbmpUnchecked := 0;
       FMnuItmAbout.dwTypeData := PChar('О форме');
       InsertMenuItem(GetSystemMenu(Self.Handle, False), 0, True, FMnuItmAbout);

       //регистрация новой процедуры обработки сообщений окна (формы)
       FNewWndProc := Pointer(@NewWndProc);
       FOldWndProc := Pointer(GetWindowLong(Self.Handle, GWL_WNDPROC));
       SetWindowLong(Self.Handle, GWL_WNDPROC, Longint(FNewWndProc));
    end;

    destructor TBaseForm.Destroy;
    begin
       inherited;
    end;

    function TBaseForm.GetOldWindowProc: Pointer;
    begin
       Result := FOldWndProc;
    end;

    procedure TBaseForm.ShowFormInfo;
    var
       InfStr: string;
    begin
       InfStr := GetAboutString;
       MessageBox(Self.Handle, PChar(InfStr), PChar('Сведения о форме'), MB_OK);
    end;

    function TBaseForm.GetAboutString: string;
    begin
       Result := 'Имя класса формы:  ' + Self.ClassName + #13 +
        'Имя формы:  ' + Self.Name + '  версия  ' + IntToStr(FVersion);
    end;

    ////////////////////////////////////////////////////////////////
    // в данном примере новая процедура обработки сообщений
    // окна (формы) создана только для одного сообщения -
    // ловить выбор пункта системного меню "О форме"
    ////////////////////////////////////////////////////////////////
    function NewWndProc(wnd: HWND; Msg: UINT; wPrm: WPARAM; lPrm: LPARAM): LRESULT stdcall;
    var
       FrmHWND: HWND;
       wctrl: TWinControl;
       BFrm: TBaseForm;
    begin
       Result := 0;
       FrmHWND := wnd;
       wctrl := FindControl(FrmHWND);
       if ( wctrl = nil ) then Exit;
       FrmHWND := GetParentForm(wctrl).Handle;
       if ( FrmHWND = 0 ) then Exit;
       BFrm := TBaseForm(FindControl(FrmHWND));
       if ( BFrm = nil ) then
        Exit;
       Result := CallWindowProc(BFrm.GetOldWindowProc, wnd, Msg, wPrm, lPrm);
       case ( Msg ) of
        //выбран пункт системного меню "О форме"
        WM_SYSCOMMAND :
         begin
          if ( LOWORD(wPrm) = ItmID ) then
          begin
             wctrl := FindControl(wnd);
             if ( wctrl <> nil ) then
              if ( wctrl is TBaseForm ) then
               TBaseForm(wctrl).ShowFormInfo;
          end;
         end;
       end;
    end;

    procedure Register;
    begin
      RegisterCustomModule(TBaseForm, TCustomModule);
    end;

    end.
Листинг 2.

Теперь, наследуя все формы проекта от класса TBaseForm, мы можем сохранять версию формы и получать информацию о форме через системное меню окна (формы).

Конечно, данным примером не исчерпывается использование данного метода. Из явных применений видятся: 1) сохранение параметров окон; 2) разграничение прав доступа (если где-нибудь, например, в базе данных, мы храним права доступа к элементам окна, то эти права можно реализовать в конструкторе TBaseForm); 3) табуляция между элементами окна по нажатию клавиши Enter в TEdit (для этого нужно в NewWndProc включить соответствующий обработчик) и т.д.

К недостаткам метода относится то, что в построенном классе TBaseForm крайне сложно будет создавать визуальные компоненты.

Вот и всё, Удачи!


    No results found.
Отменить.