Вызов форматированных всплывающих подсказок средствами Delphi

Вы наверно уже обращали внимание на форматированные подсказки при работе с редактором MS Word. Если нет, по поясню: вызываете любое диалоговое окно (например, Файл->Параметры страницы), нажимаете правой кнопкой мыши на любой компонент, в результате чего появляется выпадающее меню с текстом "Что это такое?". При щелчке на этом пункте появляется подсказка с описанием элемента управления. Подсказка может содержать форматированный текст, рисунки, гиперссылки и т.п.. Можно вызвать такую подсказку и другим способом: команда Справка->Что это такое? - в этом случае указатель мыши изменит свою форму на знак вопроса, и теперь вы можете щелкнуть на любой элемент управления, что приведет к появлению всплывающей подсказки.

Такую же технологию вы можете реализовать и в Delphi. Вам понадобится hlp-файл, содержащий сами подсказки. Для подготовки такого файла рекомендую воспользоваться программой Help & Manual. Эта программа требует платной регистрации, как и сама Delphi, поэтому думаю, что проблем с регистрацией у вас не возникнет.

Итак, после установки программа автоматически интегрируется во все продукты фирмы Borland (Delphi, Builder и т.д.). В результате в меню Tools среды Delphi появится новый пункт Help Manual Project Wizard..., а у свойства HelpContext, которое имеют многие визуальные компоненты, появится кнопочка с троеточием. Благодаря этой кнопочке процесс создания контекстной справки сильно упрощается.

Разработка контекстной справки

Контекстная справка может использоваться в двух случаях: во-первых, если вы хотите, чтобы пользователь мог увидеть подробное описание элемента управления в окне справки; во-вторых, чтобы пользователю предоставить форматированную всплывающую подсказку. В обоих случаях процесс разработки справки одинаковый. Рассмотрим этот процесс.

Создайте в Delphi простой проект, положите на форму несколько визуальных компонентов (панели, кнопочки и т.д.). После этого можете приступить к созданию файла справки. У любого визуального компонента (или у самой формы) нажмите напротив свойства HelpContext кнопку с троеточием. В результате появится окно HelpContext Property Editor. В этом окне необходимо задать расположение и имя файла проекта справки. Назовем его Popups.hm3 (если используется H & M версии 3). Нажмите ОК. Запустится программа Help & Manual. Для начала нужно выполнить следующие настройки:

  1. Определить название окна, которое будет появляться при запуске файла справки (если вы хотите использовать вызов контекстной справки). Выбираем пункт меню Project -> Project Properties -> Help Windows -> Main и заполняем поле Title bar text.
  2. Настроить русский язык. Выбираем в списке Common Properties (см. п. 1) пункт Language and Character Sets, где указываем язык Russain и набор символов RUSSIAN_CHARSET.
  3. Создать стартовую страницу, текст которой будет появляться при запуске файла справки (это актуально только для hlp-справок). Для этого в поле Invisible Topics жмем правую кнопку мыши и выбираем пункт Insert Before. В появившемся окне задаем название раздела, например "StartPage" и жмем "ОК". В поле Help Text задаем какой-нибудь текст, например: "В этом файле хранятся всплывающие подсказки".
  4. Сохраняем проект.

Теперь взгляните на поле Invisible Topics. В нем вы увидите имя компонента, для которого формируется свойство HelpContext. При нажатии на строку с этим именем шаблон подсказки появится на вкладке HelpText. В этом поле задаем текст подсказки, выполняем при необходимости форматирование (можно также вставить рисунок из буфера обмена). После этого сохраните проект с помощью команды File -> Save. Перейдите в Delphi и создайте подсказки для всех остальных элементов управления. Обратите внимание, что теперь свойства HelpContext у различных компонентов имеют разные числовые значения. Именно по этим значениям и будет отыскиваться нужный раздел в справке.

Нажмите в H & M кнопку со значком мясорубки "Make Help File and Run" и выберите пункт Winhelp, после чего нажмите ОК. В результате программа должна скомпилировать hlp-справку.

Мы попутно рассмотрим процесс вызова всплывающей подсказки, хранящейся в файле формата *.chm, поэтому, если у вас установлен "MS HTML Help Workshop", то скомпилируйте также и chm-справку (для этого нужно будет выбрать пункт HTML HELP и нажать ОК).

Все! Разработка контекстной справки успешно завершена.

Программирование вызова всплывающей hlp-подсказки с помощью контекстного меню

Применение контекстного меню - самый простой способ реализовать вызов всплывающей подсказки. Приступим к программированию.

Добавим в модуль формы переменную ContextID типа Integer и PopupFile типа String. Поместим на форму контекстное меню TPopupMenu и выделим на форме те компоненты, для которых мы хотим создать подсказки. В свойстве PopupMenu выбранной группы компонентов выбираем PopupMenu1. Если вы хотите создать подсказку для компонента, на котором лежат компоненты с пустым свойством PopupMenu, то текст его подсказки будет появляться у лежащих на нем компонентов. Это явление нужно иметь ввиду. Теперь в обработчике OnMouseDown выбранной группы пишем следующий код:

  if ssRight in Shift then ContextID := (Sender as TControl).HelpContext;

Теперь дважды щелкаем на компоненте TPopupMenu и создаем всего один пункт меню: "Что это такое?" или "What's that?". В обработчике OnClick этого пункта пишем следующий текст:

  WinHelp(Handle, PChar(PopupFile), HELP_CONTEXTPOPUP, ContextID);

где PopupFile - название файла подсказок (*.hlp). Значение этой переменной можно например задать по событию OnCreate формы следующим образом:

  if FileExists('Popups.hlp') then PopupFile := 'Popups.hlp';

Теперь вы можете посмотреть программу в действии. При нажатии правой кнопкой мыши на элементах формы должно появляться всплывающее меню с текстом "Что это такое?". При выборе этого пункта появится всплывающая подсказка.

Программирование вызова всплывающей chm-подсказки с помощью контекстного меню

Если у вас есть скомпилированный chm-файл с подсказками, то вы можете прочитать данный раздел. Отмечу, что хранить всплывающие подсказки в файле chm-формата нет смысла (дело в том, что форматированный текст и картинки в таких подсказках отображаться не будут, останется только голый текст). Кроме того, код для вызова таких подсказок значительно усложняется. Итак, изменим предыдущий код следующим образом.

В разделе interface объявите следующую функцию:

  function HtmlHelp(hwndCaller: HWND; pszFile: String; uCommand: UINT;
    dwData: DWORD): Integer; stdcall; external 'hhctrl.ocx' name 'HtmlHelpA';

Затем в разделе type следует объявить следующий тип:

  HH_POPUP = packed record
     cbStruct:    Integer;
     hinst:     HWND;
     idString:    UINT;
     pszText:   LPCTStr;
     pt:      TPOINT;
     clrForeground: COLORREF;
     clrBackground: COLORREF;
     rcMargins:   TRECT;
     pszFont:   LPCTStr;
  end;

В разделе var нужно будет объявить переменную: PopUps: HH_POPUP; а в разделе const: HH_DISPLAY_TEXT_POPUP = $000E;

В обработчике OnCreate формы нужно будет указать файл справки:

  if FileExists(ExtractFilePath(ParamStr(0)) + 'Popups.chm')  then
    PopupFile := ExtractFilePath(ParamStr(0)) + 'Popups.chm';

Теперь в обработчике OnClick компонента TPopupMenu изменяем текст следующим образом:

  procedure TForm1.N1Click(Sender: TObject);
  var
    Cur: TPoint;
  begin
    GetCursorPos(Cur);
    fillchar(PopUps, SizeOf(PopUps), 0);

    with PopUps do begin

      cbStruct := SizeOf(PopUps);
      hinst := 0;
      pt := Cur;
      idString := ContextID;
      rcMargins := Rect(5, 5, 5, 5);
      clrBackground := RGB(255, 255, 238);
    end;

     HtmlHelp(0, PopupFile + '::/CSHelp.txt',
       HH_DISPLAY_TEXT_POPUP, DWORD(@PopUps));
  end;

Еще один недостаток таких подсказок заключается в том, что функция HtmlHelp не следит за тем, поместится ли подсказка на экране полностью, а с функцией WinHelp такая проблема не возникает.

Программирование вызова всплывающей hlp-подсказки с помощью кнопки "Что это такое?" или "What's that?"

Кнопка "Что это такое?" присутствует во многих приложениях, в частности, в MS Word. Эта кнопка работает также, как работает аналогичная кнопка в заголовке окна. При нажатии на нее курсор мыши меняет свою форму на знак вопроса. В дальнейшем при щелчке мыши на каком-либо элементе интерфейса должна появляться всплывающая hlp-подсказка. Во время того, как курсор имеет форму знака вопроса, должны блокироваться нажатия любых клавиш, кроме "Escape". При нажатии клавиши "Escape", правой кнопки мыши и средней кнопки мыши нужно осуществлять отмену, т.е. устанавливать стандартный курсор. Во время режима "What's that" кнопки не должны реагировать на щелчок мыши, т.е. не должен вызываться метод OnClick.

Все это делается довольно просто. Сначала в разделе var объявляем 2 глобальные переменные:

  IsHelp: Boolean = False;
  ContextID: Integer;

У главной формы устанавливаем свойство ShowHint в True - это свойство должно быть таким же у всех визуальных компонент на этой форме. Теперь кидаем на форму панель инструментов ToolBar (Win32) и кладем на эту панель кнопку SpeedButton.

У этой кнопки изменяем следующие свойства:

  Name:  sbHelpContext;
  Caption: "?";
  AllowAllUp: True;
  GroupIndex: 1 (или другое число, отличное от нуля).

Вместо кнопки SpeedButton вы можете использовать кнопку ToolButton - в этом случае вместо свойства GroupIndex вы должны будете установить свойство Style в tbsCheck.

Дважды щелкаем на эту кнопку и пишем следующий обработчик:

  procedure TfrmMain.sbHelpContextClick(Sender: TObject);
  begin
    if sbHelpContext.Down then begin

      Screen.Cursor := crHelp;
      IsHelp := True;
      Application.HintPause := 0;
    end
    else begin
      Screen.Cursor := crDefault;
      IsHelp := False;
      Application.HintPause := 500;
    end;
  end;

Теперь кидаем на форму компонент TApplicationEvents с вкладки Additional и создаем для него 2 процедуры:

  procedure TfrmMain.ApplicationEvents1ShowHint(var HintStr: String;
    var CanShow: Boolean; var HintInfo: THintInfo);
  begin

    if IsHelp then CanShow := False;
    ContextID := HintInfo.HintControl.HelpContext;

    {Не у всех компонент можно так просто узнать значение HelpContext
     Для таких компонентов-исключений переменную ContextID нужно задавать вручную

     Пример 1:

     if (HintInfo.HintControl.ClassName = 'TNavButton') then
     ContextID := 140;

     Пример 2:
     if (HintInfo.HintControl.Name = 'DBNavigator1') then
     ContextID := 150;}
  end;

  procedure TfrmMain.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
    procedure EndHelp;
    begin

      IsHelp := False;
      Screen.Cursor := crDefault;
      sbHelpContext.Down := False;
      Handled := not Handled;
      Application.HintPause := 500;
    end;
  begin
    if IsHelp then begin

      if (MSg.message = WM_LBUTTONDOWN) or (MSg.message = WM_LBUTTONDBLCLK) then
      begin
        if (ContextID > 0) and (ContextID < 10000) then

          WinHelp(Handle, 'Popups.hlp', HELP_CONTEXTPOPUP, ContextID);
        EndHelp();
      end;

      if ((Msg.message = WM_KEYDOWN) and (Msg.wParam = 27)) or
        (MSg.message = WM_RBUTTONDOWN) or
          (MSg.message = WM_MBUTTONDOWN) then EndHelp();

      if ((Msg.message = WM_KEYDOWN) and (Msg.wParam <> 27))
        then Handled := not Handled;

    end;

  end;

Условие "if ContextID < 10000 then" необходимо, однако число может быть другим (оно не может быть меньше, чем максимальное значение HelpContext в вашей справке). Если не указать это условие, то справка будет запускаться через раз (или через два раза). В первой процедуре указывается, что в режиме "Что это такое?" родные подсказки компонентов появляться не должны.

Единственное ограничение нашего обработчика сообщений - невозможность заблокировать методы, обрабатывающие событие OnMouseMove. В этом случае программист для каждого компонента с таким методом должен будет писать следующий код (пример):

  procedure TfrmMain.BitBtn1MouseMove(Sender: TObject; Shift: TShiftState; X,
    Y: Integer);
  begin
    if IsHelp then Exit;

    // Основной код обработчика.....  
  end; 

Также вы можете создать пункт "Что это такое?" в главном меню. В этом случае обработчик выбора пункта меню будет следующим:

  procedure TfrmMain.N2Click(Sender: TObject);
  begin
    sbHelpContext.Down := True;
    sbHelpContext.Click;
  end;

©2005 Логинов Дмитрий
При перепечаке ссылка на сайт http://kladovka.net.ru обязательна.