Как работать со справочной системой в Delphi 7

Архангельский Андрей


       Как хотелось бы сделать у нового приложения стандартную справочную систему, но все советы, которые говорят — сделай так и так и все будет "ОК" — не работают. Пришлось долго "ковыряться" в документации и исходниках, чтобы все заработало и теперь стоит поделится со всеми. Здесь не будут повторятся предложения различных руководств, потому что они оказались не работоспособны в MDI-приложениях. Здесь приведены итоговые результаты, которые стабильно работают в MDI-приложениях при многочисленных вызовах дополнительных форм из дочерних.
       Замечание 1. Везде будет описываться работа в Delphi 7, только потому что предложенные рецепты в ней были проверены. Вполне возможно эти рецепты будут работать и в других версиях, но это не проверялось.
       Замечание 2. Для разработки самого Help-файла использовалась программа Help&Manual 4.0. Это одна из лучших программ, но в ней есть "ложка дегтя" – Plug-in для Delphi, который вроде помогает расставить номера topic-ов по проекту. На самом деле его использование приводит ко многим проблемам. Если программа Help&Manual только устанавливается, то нужно отметить в опциях, что этот plug-in ставить не нужно. В противном случае — ее нужно переустановить. Хотя для самого приложения неважно в какой программе сделан файл справки.
       Итак, начнем:

Работа в однооконном приложении (SDI Application)

       Это самый простой случай и в нем работает вариант, который придумала фирма Borland. А именно:
       — В свойстве формы HelpFile нужно прописать путь к файлу справочной системы. Если будет написано только имя файла, то приложение будет искать справочную систему там же где лежит приложение. Это свойство можно формировать в обработке события onFormCreate и тогда прописывать полный путь к файлу справочной системы.
       — У каждого объекта в свойстве HelpContext указать номер Topic-а, который соответствует этому Control-у. Plug-in от Help&Manual автоматически создает невидимые Topic-и для КАЖДОГО Control и сам прописывает их номера. Но, во-первых, не каждый объект требует своего Topic-а, во-вторых — часто несколько Control-ов требуют одного Topic-а. Исправлять ситуацию зачастую сложнее чем создавать заново.
       И, в принципе этого достаточно, чтобы справочная система работала. Так задумала фирма Borland. И все бы было хорошо, если бы это SDI-приложение не вызывает других форм, например, какую-нибудь модальную форму для ввода данных. И тут возникают проблемы, которые относятся к Многооконным приложениям.
       Однако, прописывать номера не очень удобно — всегда можно чего-нибудь напутать. Поэтому есть второй вариант. Использовать не номера, а TopicID — текстовый идентификатор.
       Но, при этом нужно уничтожить следы предыдущей деятельности. Дело в том, что если в проекте (в любой из форм) было заполнено свойство HelpContext, то Delphi будет перехватывать событие onKeyDown и само обрабатывать клавишу F1. Для проверки можно использовать следующий код:

procedure TfmChild.edFieldKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
   Case Key of
      VK_F1 : Begin
            if (Sender is TWincontrol) then
            begin
              ihc := TWincontrol(Sender).HelpContext;
              if ihc > 0  then application.Helpcontext(ihc)
              else showmessage('No context sensitive help available');
            end;
         end;
      VK_F2 : Begin
            if (Sender is TWincontrol) then
            begin
              ihc := TWincontrol(Sender).HelpContext;
              if ihc > 0 then application.Helpcontext(ihc)
              else showmessage('No context sensitive help available');
            end;
         end;
   end;
end;

       Во всех руководствах написано, что этот стандартный код должен работать. Однако, по нажатию клавиши F2 он работает без проблем, а по нажатию клавиши F1 работает внутренний код, который подставил Delphi.
       Для того чтобы восстановить обработку клавиши F1 нужно перевести все формы проекта в текстовый вид и в текстовом редакторе удалить все строки типа:

HelpContext=10

       После этого можно начинать работу с клавишей F1.
       Первое, что нужно сделать — это при создании Справочной системы значения всех TopicID, которые привязаны к конкретным объектам, сделать равными именам объектов, для которых они создаются. Это не является требованием системы, но значительно уменьшает количество ошибок.
       Второе - при создании формы определить путь к файлу справочной системы.

procedure TfmChild.FormCreate(Sender: TObject;);
begin
   Application.HelpFile := ExtractFilePath(Application.ExeName)+'ApplName.hlp';
end;

       Тогда вызов справки по конкретному объекту можно сделать следующим образом:

procedure TfmChild.edFieldKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
   Case Key of
      VK_F1 : Application.HelpJump('edField');
    end;
end;

       где параметр в функции HelpJump и есть TopicID.

Работа в Многооконном приложении (MDI Application)

       Многооконное приложение отличается тем, что во-первых — каждой форме может соответствовать свой файл справочной системы, и, во-вторых — любая из форм может вызвать другую в модальном или обычном режиме, а та, в свою очередь, другую. Предыдущий пример с методом HelpJump работает в MDI приложении только в том случае, когда вызывается из дочерней формы, которая в свою очередь была вызвана из главной формы. Как только дочерняя форма вызывает какой-нибудь диалог, то Delphi передает вызов справочной системы родительскому окну, а в родительском окне HelpContext не определен, что и приводит к ошибке. Прявляется это тем, что прежде чем открытся WinHelp дважды открывает файл справки. Точнее говоря, количество открытий соответствует уровню вызова формы, т.е. если дочерняя форма вызвала другую, из которой вызывается справка, то количество открытий файла справки будет три. При этом справка открывается на теме, которая была первой в файле .CNT.
       Для того чтобы сделать работоспособную справочную систему в MDI-приложении нужно использовать в каждой форме следующий код:

Var
  fmhWND   : THandle; // Идентификатор текущего окна

Const
  HlpFile = 'Help\ChildForm.hlp'; // путь к файлу справки для этой формы
  hlp_edField1 = 1010;  // Help Context number для объекта edField1
  hlp_edField2 = 1020;  // Help Context number для объекта edField1
  hlp_edField3 = 1030;  // Help Context number для объекта edField1

       Константы, определяющие HelpContext Number для каждого TopicID лучше записать в начале в одном месте, для того чтобы было легче вносить изменения. Для этих же целей имена констант соответствуют именам объектов с добавлением префикса 'hlp_'.
       После чего в событии FormCreate в переменную fmhWND записывается идентификатор окна формы.

procedure TfmChild.FormCreate(Sender: TObject);
begin
  fmhWND := self.Handle;
end;

       В событии FormClose осуществляется прямой выход WinHelp для закрытия текущей справочной системы.

procedure TfmChild.FormClose(Sender: TObject;  var Action: TCloseAction);
begin
   WinHelp(fmhWND,PChar(ApplPath+HlpFile),HELP_QUIT,0);
end;

       И, наконец, для каждого объекта в событии KeyDown записывается обработка клавиши F1:

procedure TfmChild.edField1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
   Case Key of
      VK_F1 : WinHelp(fmhWND,PChar(ApplPath+HlpFile),HELP_CONTEXT,hlp_edField1);
    end;
end;

Вызов справочной системы из меню

       И, наконец, для того чтобы вызвать содержание справочной системы из меню главной формы, требуется в обработчик пункта меню записать следующий код:

procedure TMainForm.mnHelpMainClick(Sender: TObject);
begin
   Application.HelpFile := ApplPath+'Help\PriceService.hlp';
   Application.HelpCommand(HELP_FINDER, 0);
end;

© 05.04.2006, Архангельский А.Г.




Поддержите культуру
ЯндексЯндекс. ДеньгиХочу такую же кнопку

Google
 
Web azdesign.ru az-libr.ru


Дата последнего изменения:
Wednesday, 23-Oct-2013 09:02:56 UTC