.

Программируем MainMenu DelphiЗдравствуйте уважаемые читатели моего, блога. Сегодня я хочу вам поведать о том, как программировать компонент MainMenu.

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

Общая схема…

Общая схема можно описать следующим образом:

  • Лаунчер при проверки обновления программы, проверяет папку «manual», а точнее ее содержимое. Он сверяет список инструкции в наличии и те что базе. Если чего нахватает то докачает, если файл изменился, то есть его md5-хэш не совпадет с тем, что прописан в базе, снова качаем.
  • В существующее меню программы добавляем пункт «Инструкции», и заполняем его подпунктами с названиями инструкций.
  • Для каждого пункта прописываем событие на открытие файла.
  • Подсвечиваем пункт меню. (Эта обновление задачи пришло позже, так как начальник операторов пожаловался, что якобы пользователи не замечают надпись «Инструкции», и она сливается внешне с остальными пунктами)

Начинаем программировать…

Описанный код, лучше размещать в событии FormShow, хотя можно и в FormCreate. Ниже я приведу код, создания пункта с подпунктами.

procedure TForm1.FormShow(Sender: TObject);
var
m,pm,sm:<b>TMenuItem</b>;
i:Integer;

begin
 if Menu.Items.IndexOf(Menu.Items.Find('Инструкции'))=-1 then
 begin
  m := TMenuItem.Create(Menu);
  m.Caption := 'Инструкции';
  menu.Items.Add(m);
  m.MenuIndex:=Menu.Items.IndexOf(Menu.Items.Find('Смена местоположения'));
  m.OnDrawItem := ManualDrawItem;

  ZQuery1.SQL.Clear;
  ZQuery1.SQL.Add('SELECT * FROM avtor.`tbl_update` WHERE ver='+Chr(39)+'manual'+Chr(39));
  ZQuery1.Open;

  CreateDirectory(pchar(ExtractFilePath(Application.ExeName)+'manual\'), nil);

  for i:=0 to DataModule6.ZQuery1.Recordcount-1 do
  begin
    pm := TMenuItem.Create(m);
    pm.Caption := DataModule6.ZQuery1.FieldByName('opisanie').AsString;
    pm.Name:='N'+DataModule6.ZQuery1.FieldByName('id').AsString;
    pm.OnClick:= SomeMenuItemClick;
    m.Insert(i,pm);
    DataModule6.ZQuery1.Next;
  end;
 end;
end;

Итак, изначально, нам нужно проверить существование пункта. Нумерация пунктов, меню начинается с 0.

 if Menu.Items.IndexOf(Menu.Items.Find('Инструкции'))=-1 then

Сначала  создаем пункт меню. И присваиваем, ему название «Инструкции».

  m := TMenuItem.Create(Menu);
  m.Caption := 'Инструкции';

Добавляем созданный пункт в наше меню. И определяем его местоположение, указание названия пункта рядом с которым должен располагаться наш.

  menu.Items.Add(m);
  m.MenuIndex:=Menu.Items.IndexOf(Menu.Items.Find('Смена местоположения'));

В этой строке мы определяем нашу процедуру прорисовки пункта меню. Важно, что код сработал нужно в свойствах MainMenu для свойства OwnerDraw выставить True. Этим свойством мы определяем возможность самостоятельного от рисования пунктов.

   m.OnDrawItem := ManualDrawItem;

В общем, надпись пункта MainMenu, будет отрисован чуть полужирным шрифтом, но главное он будет выделен красным цветом. Просто пользователи просили, чтобы в глаза бросался этот пункт меню.

procedure TForm1.ManualDrawItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
var
  M: TMenuItem;
begin
  M := TMenuItem(Sender);

  with ACanvas do
  begin
    Font.Size := 8;
    Font.Name :=  'Tahoma';
    Font.Height := -11;
    Font.Style := [fsbold];
    Font.Color := clRed;
    DrawText(Handle, PChar(M.Caption), Length(M.Caption), ARect,DT_SINGLELINE+DT_VCENTER+DT_CENTER);
  end;
end;

В общем получается, вот такой пункт MainMenu,после применения нашей процедуры отрисовки (ManualDrawItem).
пример MainMenu

Здесь описан запрос к таблице файлов программы, с этой таблицей работает лаунчер. Он описан в другой статье. Суть проста, вместо версии стоит слово «manual», в поле Описания, пишем текст, который будет выводиться в названии подпункта, пункта «Инструкции».

  ZQuery1.SQL.Clear;
  ZQuery1.SQL.Add('SELECT * FROM avtor.`tbl_update` WHERE ver='+С+'manual'+С);
  ZQuery1.Open;

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

  CreateDirectory(pchar(ExtractFilePath(Application.ExeName)+'manual\'), nil);

В цикле происходит создание подпунктов, пункта «Инструкции»: для этого при создании пункта pm вместо Menu, в качестве родителя передаем, переменную, указывающую на наш пункт (m).

  for i:=0 to DataModule6.ZQuery1.Recordcount-1 do
  begin
   pm := TMenuItem.Create(m); //Здесь мы определяем , что родителем будет пункт «Инструкци»
   pm.Caption := DataModule6.ZQuery1.FieldByName('opisanie').AsString; //Название файла
   pm.Name:='N'+DataModule6.ZQuery1.FieldByName('id').AsString;
   pm.OnClick:= SomeMenuItemClick; //Процедура события клика по пункту
   m.Insert(i,pm);
   DataModule6.ZQuery1.Next;
  end;

В процедуре события клика по пункту меню (SomeMenuItemClick), мы будем проверять, какой файл нужно открыть. В таблице обновлений у нас есть, физическое имя файл и мы знаем, что он лежит в директории manual.

  TForm1 = class(TForm)
  …
  procedure SomeMenuItemClick(Sender: TObject); //Вставляется ручками
  …
  private

  public

  end;

const
 C=#39;
…

procedure TForm1.SomeMenuItemClick(Sender: TObject);
var
 Word,Excel: variant;
 pa:String;
 S,ph:String;
begin
 S:=TMenuItem(Sender).Name;
 S:=copy(s,2,Length(s));

 DataModule6.ZQuery1.SQL.Clear;
 DataModule6.ZQuery1.SQL.Add('SELECT * FROM avtor.`tbl_update` WHERE id='+C+s+C);
 DataModule6.ZQuery1.Open;

 ph:=ExtractFileExt(DataModule6.ZQuery1.FieldByName('name').AsString);
 if ph<>'.avi' then
 begin
  pa:=ExtractFileDir(Application.ExeName)+'\manual\'+
  DataModule6.ZQuery1.FieldByName('name').AsString;
  try
   Word := CreateOleObject('Word.Application');
   Word.Documents.Open(pa);
  except
   ShowMessage('Не могу запустить Microsoft Word');
  end;
   Word.Visible := True;
 end
 else
 begin
  pa:=ExtractFileDir(Application.ExeName)+'\manual\'+
  DataModule6.ZQuery1.FieldByName('name').AsString;
  ShellExecute(0,'open', Pchar(pa), nil, nil, SW_SHOWNORMAL);
 end;
end;

Думаю, комментарии тут излишни. Код простой и понятный.
В заключение хотелось бы попросить не закидывать меня «гнилыми помидорами», данный пример можно использовать, как учебный для разбора основ. Он далек от идеального и рабочего. Но вам не чего не помешает переделать его под свои задачи.
С уважением, ваш Shinobi.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *