четверг, 19 марта 2009 г.

Создание приложений для Windows Vista в Delphi - часть 1

Это перевод Creating Windows Vista Ready Applications with Delphi. Автор: Nathanial Woolls.

Прим. пер.: речь идёт в основном о Delphi до 2007.

Итак, ваши приложения "работают"

К настоящему времени, надеюсь, Вы, как ответственный разработчик приложений, загрузили несколько из публичных бета-версий Microsoft Windows Vista, и убедились, что ваши приложения (и среда!) работают в новой ОС.

Хотя 99% всех хорошо написанных Delphi приложений будет работать под Vista без проблем (проблемы типа требования админских прав, элевации и UAC находятся за пределами этой статьи), они не имеют никаких бонусов с новых возможностей Windows Vista. Фактически, чем больше вы будете смотреть на новые возможности пользовательского интерфейса в Vista, тем больше ваши приложения будут смотреться как древние динозавры.

Но не беспокойтесь! Вполне возможно (и достаточно просто) преодолеть эти расхождения, и сделать ваши приложения выглядящими и "чувствующими" себя как дома с новым пользовательским интерфейсом Windows Vista. Давайте посмотрим на типичные проблемы приложений Delphi, и что мы можем сделать, чтобы улучшить наши программы.

Наше приложение-пример

Приложение-пример (sample application), над которым мы будем работать в этом посте, будет простой программой текстовым редактором типа Блокнота, с несколькими формами и действиями. Оно позволит вам загружать/сохранять текстовые файлы, а также показывать диалог "О программе" ("About" dialog). Оно также будет спрашивать вас сохранить ваши изменения, когда вы открываете файл или выходите из программы, если вы ещё не сохранили документ.


Скачать приложение-пример - шаг 1 (ссылка ведёт на статью-оригинал).

Итак, хотя наше приложение просто работает под Windows Vista, под капотом всё еще есть несколько проблем.

О, нет... только не снова это (или: Новые шрифты)

Первая проблема, которую и проще всего исправить - и которая больше всего занимает времени в уже готовых больших проектах - это шрифт. В Windows Vista шрифт по-умолчанию для пользовательского интерфейса становится Segoe UI. Только это ещё половина проблемы. Самое интересное, что не только появился новый шрифт, но и размер шрифта по-умолчанию увеличился. По-умолчанию, приложения в Windows XP использовали Tahoma 8. Приложения в Windows Vista (которые "вписываются"), однако, используют Segoe UI 9. Это означает, что не только нам надо сменить свои шрифты (если нас запустили на Windows Vista), но нам также нужно проверить каждую форму, чтобы убедиться, что весь текст видимый и при необходимости делать нужные корректировки.

Давайте начнём с создания нового модуля с именем uVistaFuncs.pas. В него мы будем добавлять специфичные для Vista методы. Для начала мы добавим методы для проверки - а не запущены ли мы под Windows Vista, и для применения правильного шрифта и размера шрифта для указанной формы. Тогда, в FormCreate главной формы и нашей диалоговой форме "О программе" мы просто вызовем SetVistaFonts(Self), предварительно убедившись, что модуль uVistaFuncs вписан в uses.

Вы заметите, что заголовок в диалоговом окне "О программе" всё ещё использует шрифт по-умолчанию: Tahoma. Это от того, что мы меняли шрифт в режиме проектирования и поэтому свойство ParentFont стало False. Вы часто будете встречаться с такой ситуацией в своих программах. Чтобы проще всего было находить такие косяки - лучше всего вписать в uVistaFuncs “Segoe Script” вместо “Segoe UI”. При этом старый шрифт в запущенной программе заметить будет гораздо проще. В нашем случае нам нужно было просто присвоить TitleLabel.Font.Name после вызова SetVistaFonts.


Скачать приложение-пример - шаг 2 (ссылка ведёт на статью-оригинал).

Прим. пер.: в Delphi в модуле Graphics есть глобальная переменная DefFontData, в которой содержится шрифт по-умолчанию. Поэтому вместо вызова функции установки шрифтов в каждом окне (SetVistaFonts) можно рассмотреть вариант установки свойства ParentFont формы в True и модификации DefFontData в initialization своего модуля (для Vista: Name := 'Segoe UI'; Height := -12;). Кстати, в старых версиях Delphi DefFontData - это всегда "MS Sans Serif" размером 8. В новых (вроде бы начиная с D2006) - это или "MS Sans Serif" 8 или системный шрифт "MS Shell Dlg 2" (доступен только в Windows Vista), если его удалось получить. Кроме того, вместо использования намертво зашитых шрифтов (типа Tahoma, MS Sans Serif, MS Shell Dlg 2 и Segoe UI) может быть лучше спросить систему о шрифте для UI. Этот подход в деталях разобран здесь.

Где моя индукция (или: зачем секретное окно)?

Windows Vista представляет несколько новых элементов, которые помогают улучшить взаимодействие пользователя с системой. Вы можете прочитать (слегка устаревшую) статью про индуктивный пользовательский интерфейс (inductive user interfaces). Фактически, индуктивный пользовательский интерфейс помогает пользователю ориентироваться в том, что происходит на экране.

Некоторые примеры этого в Windows Vista включают в себя новую анимацию минимизации и максимизации окон. Как заметил в своём интервью Ales Holecek, многие пользователи не понимают концепции окон и связи окна с кнопкой в панели задач. Они не понимают, куда девается окно, когда оно минимизируется и остаётся только кнопка на панели задач. Чтобы исправить это (и покрасоваться новым 3D UI - Aero) в Vista, окна гладко анимируются в 3D в, и из, своих кнопок в панели задач при минимизации или максимизации.

Давайте. Попробуйте сами... но только не со своими Delphi приложениями! Если вы минимизируете или максимизируете приложение Delphi в Vista, то анимация будет как у закрывающегося и открывающегося окна: окно пропадает (fades away) или плавно появляется. Причина в том, что каждое приложение Delphi имеет скрытую секретную "форму application". Это Тайное Окно является владельцем кнопки в панели задач, которую вы видите у своих Delphi программ (и оно же является причиной, почему контекстные меню кнопок панели задач у Delphi приложений отличны от контекстных меню других программ). Когда вы сворачиваете или восстанавливаете свои формы в Delphi, внутренняя магия Delphi вклинивается в эти сообщения и вместо этого просто скрывает или показывает окна. Ваши формы на самом деле никогда по настоящему не минимизируются и не восстанавливаются в истинном смысле системы.

Давайте отвлечёмся на один момент для того, чтобы обсудить еще одну особенность Windows Vista. Мы скоро вернёмся к нашему Тайному Окну. Итак, эта новая возможность в Vista (она также является элементом индуктивного интерфейса) позволяет нам увидеть предпросмотр окна просто проведя мышью по кнопке приложения в панели задач. Это облегчает поиск нужной кнопки в панели задач, да и вообще смотрится, пожалуй, здорово. Та же техника используется и при прорисовке нового Flip 3D - 3D-представления классического экрана ALT+TAB. Если вы опробуете эти возможности на обычном, развёрнутом Delphi-приложении, то всё будет работать корректно. Однако, если вы попробуете повторить это со свёрнутым приложением, то увидите пустое окно с иконкой приложения в центре вместо нормального предпросмотра.

Предпросмотр - развёрнутое приложение Delphi


Предпросмотр - свёрнутое приложение Delphi


Flip 3D со свёрнутым приложением Delphi

Почему наши Delphi программы так плохо себя ведут? Наверное, это какой-то заговор Microsoft? Нет! Это снова Тайное Окно! Когда вы двигаете мышью по кнопке панели задач и форма развёрнута, то в предпросмотре показывается содержание активного окна. Однако, когда все формы минимизированы, то скрытая “форма application” показывается на предпросмотре.

Теперь, давайте посмотрим, как мы можем исправить проблему! Мы пойдём способом, описанным Peter Below. Первое, что нам нужно сделать - избавиться от кнопки в таскбаре от нашей скрытой "формы application". В FormCreate нашего приложения мы должны поменять флаги нашей "формы application":
ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE, GetWindowLong(Application.Handle, GWL_EXSTYLE) and not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);
ShowWindow(Application.Handle, SW_SHOW);
Потом для каждой формы, для которой мы хотим иметь кнопку на панели задач (как минимум это будет главная форма), нам нужно переопределить CreateParams:
procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle and not WS_EX_TOOLWINDOW or WS_EX_APPWINDOW;
end;
Наконец, для каждой формы, которая имеет кнопку в панели задач и может минимизироваться, нам надо обрабатывать оконное сообщение WM_SYSCOMMAND:
interface

...

  protected
    procedure WMSyscommand(var Message: TWmSysCommand); message WM_SYSCOMMAND;

...

implementation

...

procedure TMainForm.WMSyscommand(var Message: TWmSysCommand);
begin
  case (Message.CmdType and $FFF0) of
    SC_MINIMIZE:
    begin
      ShowWindow(Handle, SW_MINIMIZE);
      Message.Result := 0;
    end;
    SC_RESTORE:
    begin
      ShowWindow(Handle, SW_RESTORE);
      Message.Result := 0;
    end;
  else
    inherited;
  end;
end;
Это заблокирует внутренние механизмы Delphi, которые заменяют сворачивание и разворачивание формы на скрытие и показ. Теперь наши формы должны вести себя корректно. Они правильно анимируются при минимизации/восстановлении, и они правильно отображают свой предпросмотр.

Однако, у нас осталась ещё одна серьёзная проблема. Если мы запустим наш пример и нажмём Help/About, а потом щёлкнем по нашей кнопке в панели задач, то главная форма покажется поверх нашего диалога! Потому что, по-умолчанию, Delphi устанавливает невидимую форму приложения как родителя всем модальным формам. Чтобы исправить это, вы должны установливать Form.PopupParent перед каждым вызовом Form.ShowModal:
procedure TMainForm.AboutActionExecute(Sender: TObject);
begin
  AboutForm := TAboutForm.Create(Self);
  try
    AboutForm.PopupParent := Self;
    AboutForm.ShowModal;
  finally
    FreeAndNil(AboutForm);
  end;
end;
Скачать приложение-пример - шаг 3 (ссылка ведёт на статью-оригинал).

Пусть ваши приложения сияют

Если только вы не свалились с луны, то знаете, что приложения, запущенные под Windows Vista (на машинах с графическим 3D-акселератором), рисуются с полупрозрачными, стекло-подобными рамкой и неклиентской частью. Это называется, метко, Стеклом. В DWM (Desktop Windows Manager) есть API функции, которые помогут вам расширить поверхность Стекла внутрь клиентской области вашего приложения. Хотя это не всегда бывает нужно, но может иногда помочь вашему приложению больше вписываться в образ “Vista-программы”.

Мы начнём с того, что добавим несколько новых функций в наш файлик uVistaFuncs.pas: CompositingEnabled и ExtendGlass. Исходники функций основаны на этом коде. Функция CompositingEnabled возвращает вам, есть ли поддержка 3D возможностей для Стекла, а ExtendGlass позволяет вам расширить поверхность Стекла на клиентскую поверхность наших форм. Затем мы добавим панель снизу нашей формы. Это нужно из-за того, что API вызовы DWM для расширения Стекла требуют чёрной поверхности для рисования. Наконец, мы добавим метод в нашу главную форму, который будет проверять, доступно ли Стекло и, если да, то менять цвет панели на чёрнуй и расширять поверхность Стекла на нашей главной форме:


Скачать приложение-пример - шаг 4 (ссылка ведёт на статью-оригинал).

Task Dialogs

Windows Vista представляет новый элемент пользовательского интерфейса под названием Task Dialog. Он соединяет много различных диалогов в единый API для согласованнанности. Хотя с помощью Task Dialog API можно создать невероятно сложный диалог - мы начнём с простой замены для MessageDlg. Наш метод будет использовать Vista Task Dialogs, если они доступны, или же вызов старого MessageDlg, когда их нет. Здесь есть прекрасный пример того, как это сделать (бесплатный вариант компонентов Task Dialogs).

Как только мы добавили новый метод для использования Task Dialogs в наш модуль uVistaFuncs.pas, то одно простое изменение метода SaveHandled позволит нашему запросу как лучше соответствовать пользовательскому опыту в Vista, так и обеспечить более тщательную обратную связь с конечным пользователем.

Сообщение в обычном стиле


Сообщение Task Dialog в новом стиле Windows Vista

Скачать приложение-пример - шаг 5 (ссылка ведёт на статью-оригинал).

Рисунок - это Всё

Мы почти добрались. Это изменение нам вообще-то следовало сделать давно - когда мы портировались с Windows 2000 до Windows XP. Нам нужно заменить наши плоские, пикселизованные значки в нашем приложении на красивые, сглаженные и, по возможности, с alpha-тенью. Я обнаружил, что наилучшим способом добиться этого является использование PngComponents и TPngImageList (прим. пер.: другой проект - TPNGImage - входит в состав Delphi, начиная с Delphi 2009). Простое изменение TImageList на TPngImageList позволит нам загружать PNG-картинки с alpha-каналом и использовать их в нашем приложении.


Скачать приложение-пример - шаг 6 (ссылка ведёт на статью-оригинал).

Новые общие диалоги

Windows Vista также представляет новые общие диалоги, включая переработанные диалоги открытия и сохранения. Эти новые диалоги включают в себя несколько новых возможностей Windows Vista, включая новую область ссылок избранное (её можно настраивать через щелчок правой кнопкой), новые виды и сворачиваемый диалог сохранения.

Диалог сохранения в старом стиле


Диалог сохранения в новом стиле - свёрнут


Диалог сохранения в новом стиле - развёрнут


К сожалению, приложения, написанные в Delphi, не будут автоматически использовать новые стили для диалогов открытия и сохранения. Происходит это из-за наличия флагов в Dialogs.pas, а именно: OFN_ENABLEHOOK и OFN_ENABLETEMPLATE. См. также отчёт Quality Central №34467.

Для обхода этой проблемы:
  1. Во-первых, сделайте копию Dialogs.pas и положите её в папку к своему приложению или куда-нибудь по путям в library path.
  2. Во-вторых, в этой копии Dialogs.pas (не в оригинале!), найдите “OFN_ENABLEHOOK”. Вы увидите строчку кода “Flags := OFN_ENABLEHOOK;”. Закомментируйте её.
  3. Добавьте строчку “Flags := 0;” сразу после кода, который вы только что закомментарили.
  4. Найдите “OFN_ENABLETEMPLATE”. Закомментарьте строки, начинающиеся с двух строк выше первого OFN_ENABLETEMPLATE, “if Template <> nil then”, а заканчивающиеся на “hWndOwner := Application.Handle;”. Должно быть примерно 20 строчек кода.
  5. Добавьте строку “hWndOwner := ParentWnd;” сразу после кода, который вы только что закомментарили.
  6. Перейдите в метод “TCommonDialog.Execute”. Закомментарьте “if”, “begin”, “end”, “else” и “ParentWnd := Application.Handle”. У вас должен остаться код, который всегда пытается установить сперва ParentWnd, а затем - Application.Handle.
  7. Сохраните изменения в своей копии Dialogs.pas.
Наше тестовое приложение теперь должно использовать диалоги открытия и сохранения в новом стиле. Первый шаг позволяет нам использовать модифицированную копию VCL без необходимости изменения оригинального файла. Шаг два удаляет первый проблемый флаг, но также приводит к тому, что диалоги не будут центрироваться. Шаг три - это дополнение шага два, очистка начального флага в 0. Шаг четыре удаляет второй проблемный флаг, плюс код центровки, который теперь не работает. Шаг шесть исправляет проблему центрирования, делая активную форму родителем диалогов.

Поскольку этот пункт включает в себя модификацию исходных текстов VCL, я не могу опубликовать здесь исходник. Однако, следуя шагам, описанным выше, вы без труда сделаете поддержку нового стиля диалогов для всех своих проектов без модификации их кода. Если вы не хотите делать изменения Dialogs.pas, то см. эту страницу - как установить новый стиль диалога с помощью вызовов API (прим. пер.: изменённый код Dialogs.pas не будет использоваться, если вы собираете приложение с пакетами).

Скачать заключительное приложение-пример (ссылка ведёт на статью-оригинал).

Заключение

Ниже приведены ссылки на статьи и учебные материалы по Windows Vista и пользовательским интерфейсам:
What’s New in Windows Vista
Top Rules for the Windows Vista User Experience
Top Guidelines Violations
Top 10 Ways to Light Up Your Windows Vista Apps
Using the new Windows Vista Task Dialog
The new File Open / Save Dialogs in Windows Vista
Taking the New Windows Vista Task Dialog One Step Further

Читать далее: Создание приложений для Windows Vista в Delphi - часть 2

9 комментариев:

  1. Спасибо за переводы.=)

    Может я чего-то не понимаю, но эта статья мне кажется очень странной.

    Вот например, у меня есть Delphi 6 под Вистой. Я ничего специально не далал, но:
    1) Glass frame у главной формы есть у всех моих приложений.
    2) "Новые общие диалоги" и Task Dialogs - используют Виста стиль, после того как я подключаю к проекту XpManifest.

    А ты сам пробовал следовать этой статье?

    ОтветитьУдалить
  2. Кстати, проекты собранные в Delphi 2009, как минимум нормально минимизируются/максимизируются под Вистой.

    ОтветитьУдалить
  3. 1. Читай внимательнее, речь идёт о расширении области стекла в клиентскую часть.
    2. Приложения, собранные в D6 с манифестом получают СТАРЫЙ стиль диалога (слева большие кнопки "Рабочий Стол", "Мой Компьютер"), который просто визульно в Vista выглядит по-другому. Диалог в новом стиле выглядит не так - внимательнее см. на скриншоты.

    Кстати, в оригинале не указано точное время написания статьи, но вероятно, что речь идёт о чём-нибудь вроде D6-7.

    > проекты собранные в Delphi 2009, как минимум нормально минимизируются/максимизируются под Вистой.
    Если учесть, что поддержка Vista появилась ещё в D2007, то, наверное, это не удивительно?

    ОтветитьУдалить
  4. 1. Да, точно.
    2. Перепроверил. И тут ты тоже прав.

    Насколько я понимаю, эта статья относится к Delphi 2006/2007. Ибо свойства TForm.PopupParent ни в Delphi 6 ни в Delphi 7 нет.

    ОтветитьУдалить
  5. Оригинальную статью к сожалению удалили, ссылки теперь мертвые. Алексей, у вас не сохранилось архивов?

    ОтветитьУдалить
  6. Сорри, Александр, я у вас фамилию с именем перепутал ((

    pda, спасибо! Не знал, что они архивы тоже выкачивают.

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.