Поделиться через


Использование визуального слоя с XAML WinUI

Большинство приложений WinUI и Windows App SDK, использующих возможности визуального слоя, будут использовать XAML для определения основного содержимого пользовательского интерфейса. WinUI предоставляет функции платформы XAML и визуального слоя, которые упрощают объединение этих двух технологий для создания потрясающих пользовательских возможностей. Функции взаимодействия XAML и визуального слоя можно использовать для создания расширенных анимаций и эффектов, недоступных только с помощью API XAML. Сюда входит следующее:

  • Эффекты кисти, такие как размытие и заморозка стекла
  • Динамические эффекты освещения
  • Анимации на основе прокрутки и параллакс
  • Анимации автоматически настроенных макетов
  • Точные теневые эффекты

Эти эффекты и анимации можно применять к существующему содержимому XAML, поэтому вам не нужно резко реструктурировать приложение WinUI, чтобы воспользоваться преимуществами функций. Анимации макета, тени и эффекты размытия рассматриваются в разделе "Рецепты" ниже. Пример кода, реализующий параллакс, см. в примере ParallaxingListItems. Репозиторий WindowsCompositionSamples также содержит несколько других примеров для реализации анимаций, тени и эффектов.

Класс XamlCompositionBrushBase

Microsoft.UI.Xaml.Media.XamlCompositionBrushBase предоставляет базовый класс для кистей XAML, которые рисуют область с помощью CompositionBrush. Это можно использовать для легкого применения эффектов композиции, таких как размытие или заморозка стекла к элементам пользовательского интерфейса XAML.

Дополнительные сведения об использовании кистей с пользовательским интерфейсом XAML см. в разделе "Кисти ".

Примеры кода см. на справочной странице xamlCompositionBrushBase.

Класс XamlLight

Microsoft.UI.Xaml.Media.XamlLight предоставляет базовый класс для эффектов освещения XAML, динамически освещающих область с помощью CompositionLight.

Дополнительные сведения об использовании освещения, включая элементы пользовательского интерфейса XAML, см. в разделе "Освещение ".

Примеры кода см. на справочной странице XamlLight.

Работа с WinUI XAML

ElementCompositionPreview — это статический класс, предоставляющий функции взаимодействия XAML и визуального слоя. Общие сведения о визуальном слое и его функциональных возможностях см. в разделе "Визуальный слой". Класс ElementCompositionPreview предоставляет следующие методы взаимодействия WinUI:

  • GetElementVisual: получение визуального элемента "раздаточного" элемента, используемого для отрисовки этого элемента
  • SetElementChildVisual: устанавливает визуальный элемент "handin" как последний дочерний элемент в визуальном дереве этого элемента. Этот визуальный элемент будет отображаться поверх других частей элемента.
  • GetElementChildVisual: получение визуального набора с помощью SetElementChildVisual
  • GetScrollViewerManipulationPropertySet: получение объекта, который можно использовать для создания анимаций с частотой 60 кадров в секунду на основе смещения области прокрутки в ScrollViewer

GetElementVisual

ElementCompositionPreview.GetElementVisual возвращает "раздаточный" элемент Visual, который используется для рендеринга данного UIElement. Свойства, такие как Visual.Opacity, Visual.Offset и Visual.Size , задаются платформой XAML на основе состояния UIElement. Это позволяет использовать такие методы, как неявная анимация перемещения (см. рецепты).

Обратите внимание, что так как в результате макета платформы XAML заданы смещение и размер , разработчики должны быть осторожны при изменении или анимации этих свойств. Разработчики должны изменять или анимировать Offset только когда верхний левый угол элемента находится в том же положении, что и его родительский элемент в макете. Размер обычно не следует изменять, но доступ к свойству может оказаться полезным. Например, примеры Drop Shadow и Frosted Glass ниже используют размер визуальных элементов раздаточного материала в качестве входных данных для анимации.

В качестве дополнительной оговорки обновленные свойства визуального элемента раздаточного элемента не будут отражены в соответствующем UIElement. Например, если для параметра UIElement.Opacity задано значение 0.5, для соответствующего раздаточного элемента Visual Opacity задано значение 0,5. Тем не менее, при установке Opacity элемента Visual в 0,5 содержимое будет отображаться с непрозрачностью в 50%, но значение соответствующего свойства Opacity элемента UIElement не изменится.

Пример анимации смещения

Неправильный

<Border>
      <Image x:Name="MyImage" Margin="5" />
</Border>
// Doesn’t work because Image has a margin!
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);

Исправить

<Border>
    <Canvas Margin="5">
        <Image x:Name="MyImage" />
    </Canvas>
</Border>
// This works because the Canvas parent doesn’t generate a layout offset.
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);

УстановитьДочернийВизуальныйЭлемент

ElementCompositionPreview.SetElementChildVisual позволяет разработчику предоставить визуальный элемент "handin", который будет отображаться как часть визуального дерева элемента. Это позволяет разработчикам создавать "Остров композиции", где визуальное содержимое может отображаться в пользовательском интерфейсе XAML. Разработчики должны быть консервативными в использовании этого метода, так как содержимое на основе визуальных элементов не будет иметь одинаковых специальных возможностей и гарантий пользовательского интерфейса содержимого XAML. Поэтому обычно рекомендуется использовать этот метод только при необходимости для реализации пользовательских эффектов, таких как те, которые приведены в разделе "Рецепты" ниже.

Методы GetAlphaMask

Изображение, TextBlock и Фигура реализуют метод GetAlphaMask , возвращающий объект CompositionBrush , представляющий изображение серого уровня с фигурой элемента. Этот CompositionBrush может служить входным элементом для объекта Composition DropShadow, поэтому тень может отражать форму элемента вместо прямоугольника. Это обеспечивает пиксельно точные, контурные тени для текста, изображений с альфа-каналом и форм. Пример этого API см. в разделе Drop Shadow ниже.

Рецепты

Анимация изменения положения

Используя неявные анимации Composition, разработчик может автоматически анимировать изменения положения элемента относительно его родителя. Например, если изменить отступ кнопки ниже, кнопка автоматически анимируется в новой позиции в макете.

Обзор реализации

  1. Получение раздаточного материала Visual для заданного элемента.
  2. Создание объекта ImplicitAnimationCollection, которое автоматически анимирует изменения в свойстве Offset
  3. Связать ImplicitAnimationCollection с основным визуальным элементом
<Button x:Name="RepositionTarget" Content="Click Me" />
public MainPage()
{
    InitializeComponent();
    InitializeRepositionAnimation(RepositionTarget);
}

private void InitializeRepositionAnimation(UIElement repositionTarget)
{
    var targetVisual = ElementCompositionPreview.GetElementVisual(repositionTarget);
    Compositor compositor = targetVisual.Compositor;

    // Create an animation to animate targetVisual's Offset property to its final value
    var repositionAnimation = compositor.CreateVector3KeyFrameAnimation();
    repositionAnimation.Duration = TimeSpan.FromSeconds(0.66);
    repositionAnimation.Target = "Offset";
    repositionAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");

    // Run this animation when the Offset Property is changed
    var repositionAnimations = compositor.CreateImplicitAnimationCollection();
    repositionAnimations["Offset"] = repositionAnimation;

    targetVisual.ImplicitAnimations = repositionAnimations;
}

Эффект наложения тени

Примените тень идеального пикселя к UIElement, например эллипсу , содержащей рисунок. Так как для тени требуется SpriteVisual, созданный приложением, необходимо создать элемент-хост, который будет содержать SpriteVisual с помощью ElementCompositionPreview.SetElementChildVisual.

Обзор реализации

  1. Получите раздаточный материал Visual для хост-элемента.
  2. Создайте Microsoft.UI.Composition DropShadow
  3. Настройка DropShadow для получения фигуры из целевого элемента с помощью маски
    • DropShadow является прямоугольным по умолчанию, поэтому это не обязательно, если целевой объект прямоугольный
  4. ru-RU: Присоедините тень к новому SpriteVisual и сделайте SpriteVisual дочерним элементом узла хоста.
  5. Привязка размера spriteVisual к размеру узла с помощью ExpressionAnimation
<Grid Width="200" Height="200">
    <Canvas x:Name="ShadowHost" />
    <Ellipse x:Name="CircleImage">
        <Ellipse.Fill>
            <ImageBrush ImageSource="Assets/Images/2.jpg" Stretch="UniformToFill" />
        </Ellipse.Fill>
    </Ellipse>
</Grid>
public MainPage()
{
    InitializeComponent();
    InitializeDropShadow(ShadowHost, CircleImage);
}

private void InitializeDropShadow(UIElement shadowHost, Shape shadowTarget)
{
    Visual hostVisual = ElementCompositionPreview.GetElementVisual(shadowHost);
    Compositor compositor = hostVisual.Compositor;

    // Create a drop shadow
    var dropShadow = compositor.CreateDropShadow();
    dropShadow.Color = Color.FromArgb(255, 75, 75, 80);
    dropShadow.BlurRadius = 15.0f;
    dropShadow.Offset = new Vector3(2.5f, 2.5f, 0.0f);
    // Associate the shape of the shadow with the shape of the target element
    dropShadow.Mask = shadowTarget.GetAlphaMask();

    // Create a Visual to hold the shadow
    var shadowVisual = compositor.CreateSpriteVisual();
    shadowVisual.Shadow = dropShadow;

    // Add the shadow as a child of the host in the visual tree
   ElementCompositionPreview.SetElementChildVisual(shadowHost, shadowVisual);

    // Make sure size of shadow host and shadow visual always stay in sync
    var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
    bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);

    shadowVisual.StartAnimation("Size", bindSizeAnimation);
}

В следующем описании показан эквивалент C++/WinRT предыдущего кода C#, использующий ту же структуру XAML.

#include <winrt/Microsoft.UI.Composition.h>
#include <winrt/Microsoft.UI.Xaml.h>
#include <winrt/Microsoft.UI.Xaml.Hosting.h>
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
...
MainPage()
{
    InitializeComponent();
    InitializeDropShadow(ShadowHost(), CircleImage());
}

int32_t MyProperty();
void MyProperty(int32_t value);

void InitializeDropShadow(Microsoft::UI::Xaml::UIElement const& shadowHost, Microsoft::UI::Xaml::Shapes::Shape const& shadowTarget)
{
    auto hostVisual{ Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost) };
    auto compositor{ hostVisual.Compositor() };

    // Create a drop shadow
    auto dropShadow{ compositor.CreateDropShadow() };
    dropShadow.Color(Microsoft::UI::ColorHelper::FromArgb(255, 75, 75, 80));
    dropShadow.BlurRadius(15.0f);
    dropShadow.Offset(Windows::Foundation::Numerics::float3{ 2.5f, 2.5f, 0.0f });
    // Associate the shape of the shadow with the shape of the target element
    dropShadow.Mask(shadowTarget.GetAlphaMask());

    // Create a Visual to hold the shadow
    auto shadowVisual = compositor.CreateSpriteVisual();
    shadowVisual.Shadow(dropShadow);

    // Add the shadow as a child of the host in the visual tree
    Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(shadowHost, shadowVisual);

    // Make sure size of shadow host and shadow visual always stay in sync
    auto bindSizeAnimation{ compositor.CreateExpressionAnimation(L"hostVisual.Size") };
    bindSizeAnimation.SetReferenceParameter(L"hostVisual", hostVisual);

    shadowVisual.StartAnimation(L"Size", bindSizeAnimation);
}

Заморозка стекла

Создайте эффект, который размывает и оттеняет фоновое содержимое. Обратите внимание, что разработчикам необходимо установить пакет NuGet Win2D для использования эффектов. Инструкции по установке см. на домашней странице Win2D .

Обзор реализации

  1. Получение раздаточного материала Visual для хост-элемента
  2. Создание дерева эффектов размытия с помощью Win2D и CompositionEffectSourceParameter
  3. Создание объекта CompositionEffectBrush на основе дерева эффектов
  4. Задайте вход CompositionEffectBrush в CompositionBackdropBrush, что позволяет применять эффект к содержимому, находящемуся за SpriteVisual.
  5. Задайте CompositionEffectBrush в качестве содержимого нового SpriteVisual, и задайте SpriteVisual в качестве дочернего элемента узла. Можно альтернативно использовать XamlCompositionBrushBase.
  6. Привязка размера spriteVisual к размеру узла с помощью ExpressionAnimation
<Grid Width="300" Height="300" Grid.Column="1">
    <Image
        Source="Assets/Images/2.jpg"
        Width="200"
        Height="200" />
    <Canvas
        x:Name="GlassHost"
        Width="150"
        Height="300"
        HorizontalAlignment="Right" />
</Grid>
public MainPage()
{
    InitializeComponent();
    InitializeFrostedGlass(GlassHost);
}

private void InitializeFrostedGlass(UIElement glassHost)
{
    Visual hostVisual = ElementCompositionPreview.GetElementVisual(glassHost);
    Compositor compositor = hostVisual.Compositor;

    // Create a glass effect, requires Win2D NuGet package
    var glassEffect = new GaussianBlurEffect
    { 
        BlurAmount = 15.0f,
        BorderMode = EffectBorderMode.Hard,
        Source = new ArithmeticCompositeEffect
        {
            MultiplyAmount = 0,
            Source1Amount = 0.5f,
            Source2Amount = 0.5f,
            Source1 = new CompositionEffectSourceParameter("backdropBrush"),
            Source2 = new ColorSourceEffect
            {
                Color = Color.FromArgb(255, 245, 245, 245)
            }
        }
    };

    //  Create an instance of the effect and set its source to a CompositionBackdropBrush
    var effectFactory = compositor.CreateEffectFactory(glassEffect);
    var backdropBrush = compositor.CreateBackdropBrush();
    var effectBrush = effectFactory.CreateBrush();

    effectBrush.SetSourceParameter("backdropBrush", backdropBrush);

    // Create a Visual to contain the frosted glass effect
    var glassVisual = compositor.CreateSpriteVisual();
    glassVisual.Brush = effectBrush;

    // Add the blur as a child of the host in the visual tree
    ElementCompositionPreview.SetElementChildVisual(glassHost, glassVisual);

    // Make sure size of glass host and glass visual always stay in sync
    var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
    bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);

    glassVisual.StartAnimation("Size", bindSizeAnimation);
}

Дополнительные ресурсы