Изменение внешнего вида пользовательского интерфейса в зависимости от аппаратных возможностей в WPF

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

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

Дело в том, что есть такой класс RenderCapatibility, который имеет статическое свойство Tier. Это свойство может содержать три разных значения, в зависимости от того, какими аппаратными возможностями вы располагаете:

  • 0 – аппаратное ускорение для графики отсутствует на данном устройстве;
  • 1 – частичная поддержка графического аппаратного ускорения, поддержка DirectX версии 7.0 – 9.0;
  • 2 – полная поддержка аппаратного ускорения, поддержка DirectX версии 9.0 или выше.

Таким образом, основываясь на этом значении вы можете корректировать визуальную составляющую в вашем приложении. Однако, есть одно "но" :) Дело в том, что на самом деле это свойство выдает значения не 0, 1 и 2, а 0, 0x00010000 и 0x00020000. Как можно заметить, это те же цифры 0, 1 и 2, но сдвинутые логически на 16 разрядов. Для получения нужных нам значений необходимо произвести обратную операцию:

int renderingTier = (RenderCapability.Tier >> 16);

Теперь давайте посмотрим на то, каким образом мы можем изменять пользовательский интерфейс в зависимости от этого значения декларативно (т.е. внутри XAML).

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

public static class RenderCapabilityWrapper
{
  public static readonly DependencyProperty TierProperty =
    DependencyProperty.RegisterAttached(
    "Tier",
    typeof(int),
    typeof(RenderCapabilityWrapper),
    new PropertyMetadata(RenderCapability.Tier >> 16));

  public static int GetTier(DependencyObject depObj)
  {
    return (int)TierProperty.DefaultMetadata.DefaultValue;
  }

  public static int Tier
  {
    get
    {
      return (int)TierProperty.DefaultMetadata.DefaultValue;
    }
  }
}

Такой класс-обертка позволит нам прямо из описания пользовательского интерфейса получить значение свойства Tier и произвести изменения в пользовательском интерфейсе. Для этого лучше всего подходят триггеры.

Давайте попробуем теперь описать стиль для кнопки.

<Style TargetType="{x:Type Button}">
  <Style.Triggers>
    <Trigger Property="hardware:RenderCapabilityWrapper.Tier" Value="2">
    <!-- описание стиля для Tier = 2 -->
    </Trigger>
    <Trigger Property="hardware:RenderCapabilityWrapper.Tier" Value="1">
    <!-- описание стиля для Tier = 1 -->
    </Trigger>
    <Trigger Property="hardware:RenderCapabilityWrapper.Tier" Value="0">
    <!-- описание стиля для Tier = 0 -->
    </Trigger>
  </Style.Triggers>
</Style>

Вот так легко и просто мы можем создавать адаптивный пользовательский интерфейс для наших приложений, который изменяется в зависимости от аппаратных возможностей.