пятница, 20 марта 2009 г.

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

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

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

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

Эта статья предполагает, что вы читали первую статью и примеры кода этой статьи начнутся с того места, где мы остановились в прошлый раз.

Исправление шрифтов

Спасибо David Rose за обнаружение проблемы с SetVistaFonts(). Мне раньше казалось, что если установить в Font.Name имя несуществующего шрифта, то Font.Name должен измениться. Это не так. Наш первоначальный код проверял Font.Name, чтобы убедиться, что Segoe UI был успешно установлен шрифтом формы, что не должно было работать. Наш новый код проверяет список Screen.Fonts, чтобы убедиться, что шрифт Segoe UI доступен.
procedure SetVistaFonts(const AForm: TCustomForm);
begin
  if (IsWindowsVista or not CheckOSVerForFonts) and 
     not SameText(AForm.Font.Name, VistaFont) and 
     (Screen.Fonts.IndexOf(VistaFont) >= 0) then
  begin
    AForm.Font.Size := AForm.Font.Size + 1;
    AForm.Font.Name := VistaFont;
  end;
end;
Новый код также вводит новую глобальную переменную CheckOSVerForFonts, которая определяет, надо ли менять шрифт только под Vista или также под любой другой ОС. Первый вариант кода не имеет проблем, если только шрифт Segoe UI установлен Vista (что наиболее вероятно), но новый вариант кода более корректен и завершён.

Добавления шрифтов

На CodeGear Developer Network, Daniel England обратил внимание на факт, что имена шрифтов зашиты в программу намертво. Хотя я согласен, что это не очень хорошо, но я хотел, чтобы эти изменения согласовывались с опытом работы в Delphi. Delphi, по-умолчанию, не адаптирует шрифты форм, чтобы они соответствовали текущим шрифтам в Windows. Если бы она так делала, то нам бы не пришлось руками менять наш шрифт на Segoe UI под Vista.

Тем не менее, первое дополнение к коду шрифтов - это SetDesktopIconFonts. Эту функцию можно вызвать, передав ей экземпляр TFont, для установки параметров шрифта, равных текущим настройкам шрифта для иконок на рабочем столе в Windows. В обновленном примере, этот метод используется вместо SetVistaFonts.
procedure SetDefaultFonts(const AFont: TFont);
begin
  AFont.Handle := GetStockObject(DEFAULT_GUI_FONT);
end;

procedure SetDesktopIconFonts(const AFont: TFont);
var
  LogFont: TLogFont;
begin
  if SystemParametersInfo(SPI_GETICONTITLELOGFONT, SizeOf(LogFont), @LogFont, 0) then
    AFont.Handle := CreateFontIndirect(LogFont)
  else
    SetDefaultFonts(AFont);
end;
Этот код пытается получить настройки шрифта для иконок текущего рабочего стола, а при неудаче получает настройки шрифта по-умолчанию в Windows.

Наше второе дополнение к коду шрифтов - это SetVistaContentFonts. Эта процедура работает так же, как и SetVistaFonts, но только берёт шрифт для элементов редактирования типа memo, rich edit и т.п. При этом, новым де-факто шрифтом для этих элементов Vista является Calibri.
procedure SetVistaContentFonts(const AFont: TFont);
begin
  if (IsWindowsVista or not CheckOSVerForFonts) and 
     not SameText(AFont.Name, VistaContentFont) and 
     (Screen.Fonts.IndexOf(VistaContentFont) >= 0) then
  begin
    AFont.Size := AFont.Size + 2;
    AFont.Name := VistaContentFont;
  end;
end;
Вызов этой процедуры с передачей ей объекта TFont приведёт к тому, что имя шрифта будет изменено на Calibri, а размер шрифта увеличится, если приложение запущено под Vista (или если глобалная переменная CheckOSVerForFonts равна false) и шрифт Calibri установлен.

TreeView в стиле Vista

Ещё одним обновлённым в Windows Vista элементом пользовательского интерфейса, не упомянутым в первой статье, стал TreeView. В Windows Vista выделение в TreeView рисуется светло-синим градиентом, а не традиционным прямоугольником выделения, а, кроме того, кнопки узлов в TreeView рисуются как маленькие треугольнички, а не как плюсики.

Чтобы заставить наши TTreeView использовать новый стиль, достаточно использовать следующий простой код:
procedure SetVistaTreeView(const AHandle: THandle);
begin
  if IsWindowsVista then
    SetWindowTheme(AHandle, 'explorer', nil);
end;
Вызывая этот метод и передавая ему дескриптор TreeView, вы зададите переданному TreeView отрисовку в новом стиле под Vista.

Обходное решение для PopupParent

Наша первая статья представила решение для обхода проблемы со скрытым секретным окном application в Delphi, позволяя корректно работать нескольким новым возможностям интерфейса пользователя в Vista - таким как Flip 3D и live thumbnails. Однако эти изменения привели к тому, что нам надо устанавливать свойство PopupParent при каждом вызове ShowModal, чтобы соблюдать правильный Z-порядок наших форм.

Переписка с Эриком Фортье вынудила меня копнуть немного глубже и посмотреть, нельзя ли устранить это требование. Хотя это действительно возможно, но это требует внесения изменений в код VCL (по аналогии с нашими изменениями к Dialogs.pas в оригинале статьи):
  • Во-первых, сделайте копию Forms.pas и положите копию либо в папку к своему приложению, либо куда-то в library path.
  • Во-вторых, в этой копии Forms.pas, перейдите к строке 4032 (номер строки может отличаться для разных версий Delphi).
  • Ищите case-выражение с LPopupMode.
  • Закомментируйте первую строчку за case: "//pmNone: Application.Handle;"
  • Отредактируйте следующую строчку, которая сейчас просто ловит pmAuto, чтобы она ловила также и pmNone: "pmNone, pmAuto:"
  • Сохраните изменения в своей копии Forms.pas
  • Результирующий код должен проверять LPopupMode и действовать одинаково для pmNone и pmAuto, а не использовать Application.Handle в качестве родителя, если PopupMode равно pmNone. Это именно то, что мы хотим. Теперь, VCL попытается найти родителя формы обнаружением активного окна с немного другой логикой, а не просто использовать Application.Handle (который не будет работать из-за изменений в нашей первой статье), если PopupMode является pmNone (по-умолчанию).

    Странная активация

    Один из побочных эффектов, созданный первой статьёй при исправлении оконной анимации и предпросмотров, заключается в том, что форма под другой модальной формой может быть визуально активна, если её кнопка на панели задач была нажата. Заметим, что это только визуальное несоответствие: вы не можете реально взаимодействовать с нижней формой. Макс Пятницкий продемонстрировал эту проблему на CodeGear newsgroups, и я думаю, что я мог бы поделиться слегка измененной версией своего решения (оно также включено в обновленный пример и скомпилированный исполняемый файл):
    procedure TMainForm.WMActivate(var Message: TWMActivate);
    begin
      if (Message.Active = WA_ACTIVE) and not IsWindowEnabled(Handle) then
      begin
        SetActiveWindow(Application.Handle);
        Message.Result := 0;
      end 
      else
        inherited;
    end;
    Спасибо Максу Пятницкому за решение этой проблемы. Этот код передаёт сообщение об активации секретному окну application, когда активируется наша форма - это обеспечивает корректность активации модальных форм.

    Перебор окон

    Существует еще один подводный камень, который может повлиять на пользователей, которые использовали наш код для обхода скрытого окна application. По словам Tom Nagy на CodeGear newsgroups, разработчики, которые перечисляют на окна верхнего уровня, используя EnumWindows, могут использовать такой код для функции обратного вызова EnumWindowsProc:
    if IsWindowVisible(Wnd) and 
       ((GetWindowLong(Wnd, GWL_HWNDPARENT) = 0) or
        (HWND(GetWindowLong(Wnd, GWL_HWNDPARENT)) = GetDesktopWindow)) and
       ((GetWindowLong(Wnd, GWL_EXSTYLE) and WS_EX_TOOLWINDOW) = 0) then
    begin
      ...
    end;
    По словам Тома, это может быть довольно распространенным фрагментом кода Delphi для перечисления окон. Проблема в том, что наши изменения флагов в CreateParams нашей формы приводят к тому, что проверка на WS_EX_TOOLWINDOW будет неудачной. Том предлагает следующее изменение в коде поиска (с удалением проверки WS_EX_TOOLWINDOW), чтобы решить эту проблему:
    if IsWindowVisible(Wnd) and 
      ((GetWindowLong(Wnd, GWL_HWNDPARENT) = 0) or
       (HWND(GetWindowLong(Wnd, GWL_HWNDPARENT)) = GetDesktopWindow)) then
    begin
      ...
    end;
    Спасибо Tom Nagy за обнаружение этого момента и обсуждения его на newsgroups. Надеюсь, это поможет другим избежать этой ошибки!

    Показ комбинаций быстрого выбора по ALT

    Существует еще одна ошибка, которая ударяет по приложениям Delphi, когда они запускаются под Vista. Эту ошибку зарегистрировали на Quality Central, и известно несколько путей её обхода. Проблема заключается в том, что, когда нажимается клавиша ALT, некоторые потомки TWinControl могут исчезнуть с вашей формы до тех пор, пока им не потребуется перерисовка. Существует компонент, который исправляет эту проблему. Я попытался использовать код исправления Controls.pas, но он мне не помог. А вот этот компонент вполне сработал.

    Заключение & нерешенные вопросы

    Все эти изменения, обсуждаемые в оригинальной статье, взятые вместе, дают нам улучшенную поддержку Vista в наших приложениях. Наши шрифты будут вести себя как положено, если Segoe UI не доступен, и могут (по желанию) соответствовать шрифту значков на рабочем столе. Теперь мы можем иметь элементы редактирования с поддержкой нового стиля шрифта, а наши TreeView будут менять свой стиль, в соответствии с новым пользовательским интерфейсом в Vista.


    Ссылки на оригинал статьи:
    Скачать обновлённый пример
    Скачать обновлённое скомпилированное приложение

    Тем не менее, у нас остался один нерешенный вопрос, и какие-либо отзывы или потенциальные решения по этому вопросу очень приветствую (контактная информация находится в колонтитуле оригинальной страницы). Если объединить код поддержки анимации окон из оригинала статьи с кодом в этой статье для поддержки новых элементов пользовательского интерфейса в TreeView, то ваши приложения будут иметь небольшую, но заметную ошибку. Минимизация формы будет приводить к тому, что форма под любым окном сперва исчезает, а потом приступает к анимации сворачивания в панель задач. Эта ошибка может быть также замечена в некоторых приложениях Microsoft, так что трудно сказать, что именно может быть её причиной. Любые мысли по этому вопросу приветствуются.

    Ниже приведён расширенный список ссылок на статьи и tutorial-ы по 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
    The new File Open / Save Dialogs in Windows Vista
    Using the new Windows Vista Task Dialog
    Taking the New Windows Vista Task Dialog One Step Further
    Full Featured Multi-Platform Task Dialog Control
    Task Dialogs от Vista/Windows 7 в Windows XP

    Комментариев нет:

    Отправить комментарий

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

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

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

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

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