Compartilhar via


Usando a camada visual com XAML

A maioria dos aplicativos que consomem recursos da Camada Visual usará XAML para definir o conteúdo principal da interface do usuário. Na Atualização de Aniversário do Windows 10, há novos recursos na estrutura XAML e na Camada Visual que facilitam a combinação dessas duas tecnologias para criar experiências de usuário impressionantes. A funcionalidade de interoperabilidade XAML e Visual Layer pode ser usada para criar animações e efeitos avançados não disponíveis usando apenas APIs XAML. Isso inclui:

  • Efeitos de pincel como desfoque e vidro fosco
  • Efeitos de iluminação dinâmicos
  • Animações orientadas por rolagem e paralaxe
  • Animações automáticas de layout
  • Sombras projetadas com pixels perfeitos

Esses efeitos e animações podem ser aplicados ao conteúdo XAML existente, para que você não precise reestruturar drasticamente seu aplicativo XAML para aproveitar a nova funcionalidade. Animações de layout, sombras e efeitos de desfoque são abordados na seção Receitas abaixo. Para obter um exemplo de código que implementa a paralaxe, consulte o exemplo ParallaxingListItems. O repositório WindowsCompositionSamples também tem vários outros exemplos para implementar animações, sombras e efeitos.

A classe XamlCompositionBrushBase

XamlCompositionBrush fornece uma classe base para pincéis XAML que pintam uma área com um CompositionBrush. Isso pode ser usado para aplicar facilmente efeitos de composição, como desfoque ou vidro fosco, a elementos de interface do usuário XAML.

Consulte a seção Pincéis para obter mais informações sobre como usar pincéis com a interface do usuário XAML.

Para obter exemplos de código, confira a página de referências do XamlCompositionBrushBase.

A classe XamlLight

XamlLight fornece uma classe base para efeitos de iluminação XAML que iluminam dinamicamente uma área com um CompositionLight.

Consulte a seção Iluminação para obter mais informações sobre como usar luzes, incluindo elementos de interface do usuário XAML de iluminação.

Para obter exemplos de código, consulte a página de referência de XamlLight.

A classe ElementCompositionPreview

ElementCompositionPreview é uma classe estática que fornece funcionalidade de interoperabilidade XAML e Visual Layer. Para obter uma visão geral da Camada Visual e sua funcionalidade, consulte Camada Visual. A classe ElementCompositionPreview fornece os seguintes métodos:

  • GetElementVisual: obter um visual de "folheto" que é usado para renderizar esse elemento
  • SetElementChildVisual: define um Visual "handin" como o último filho da árvore visual desse elemento. Esse visual será desenhado sobre o restante do elemento.
  • GetElementChildVisual: recuperar o conjunto visual usando SetElementChildVisual
  • GetScrollViewerManipulationPropertySet: obtenha um objeto que pode ser usado para criar animações de 60 fps com base no deslocamento de rolagem em um ScrollViewer

Comentários sobre ElementCompositionPreview.GetElementVisual

ElementCompositionPreview.GetElementVisual retorna um Visual de "folheto" que é usado para renderizar o UIElement fornecido. Propriedades como Visual.Opacity, Visual.Offset e Visual.Size são definidas pela estrutura XAML com base no estado do UIElement. Isso permite técnicas como animações de reposicionamento implícito (consulte Receitas).

Observe que, como Offset e Size são definidos como resultado do layout da estrutura XAML, os desenvolvedores devem ter cuidado ao modificar ou animar essas propriedades. Os desenvolvedores só devem modificar ou animar o deslocamento quando o canto superior esquerdo do elemento tiver a mesma posição que a de seu pai no layout. O tamanho geralmente não deve ser modificado, mas acessar a propriedade pode ser útil. Por exemplo, os exemplos de Sombra projetada e Vidro fosco abaixo usam o tamanho de um folheto Visual como entrada para uma animação.

Como uma advertência adicional, as propriedades atualizadas do folheto Visual não serão refletidas no UIElement correspondente. Por exemplo, definir UIElement.Opacity como 0,5 definirá a Opacidade do Visual do folheto correspondente como 0,5. No entanto, definir a Opacidade do Visual do folheto como 0,5 fará com que o conteúdo apareça com 50% de opacidade, mas não alterará o valor da propriedade Opacidade do UIElement correspondente.

Exemplo de animação de deslocamento

Incorreto

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

Correto

<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);

O método ElementCompositionPreview.SetElementChildVisual

ElementCompositionPreview.SetElementChildVisual permite que o desenvolvedor forneça um Visual "handin" que aparecerá como parte da Árvore Visual de um elemento. Isso permite que os desenvolvedores criem uma "Ilha de Composição" em que o conteúdo baseado em Visual pode aparecer dentro de uma interface do usuário XAML. Os desenvolvedores devem ser conservadores ao usar essa técnica porque o conteúdo baseado em Visual não terá as mesmas garantias de acessibilidade e experiência do usuário do conteúdo XAML. Portanto, geralmente é recomendado que essa técnica seja usada apenas quando necessário para implementar efeitos personalizados, como os encontrados na seção Receitas abaixo.

Métodos GetAlphaMask

Image, TextBlock e Shape implementam um método chamado GetAlphaMask que retorna um CompositionBrush que representa uma imagem em tons de cinza com a forma do elemento. Esse CompositionBrush pode servir como uma entrada para um Composition DropShadow, para que a sombra possa refletir a forma do elemento em vez de um retângulo. Isso permite sombras perfeitas baseadas em contornos para texto, imagens com alfa e formas. Consulte Drop Shadow abaixo para obter um exemplo dessa API.

Receitas

Animação de reposicionamento

Usando Animações Implícitas de Composição, um desenvolvedor pode animar automaticamente as alterações no layout de um elemento em relação ao seu pai. Por exemplo, se você alterar a margem do botão abaixo, ele será animado automaticamente para sua nova posição de layout.

Visão geral da implementação

  1. Obter o folheto Visual para o elemento de destino
  2. Criar um ImplicitAnimationCollection que anima automaticamente as alterações na propriedade Offset
  3. Associar o ImplicitAnimationCollection ao Visual de suporte
<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;
}

Sombra

Aplique uma sombra projetada com pixels perfeitos a um UIElement, por exemplo, uma Elipse contendo uma imagem. Como a sombra requer um SpriteVisual criado pelo aplicativo, precisamos criar um elemento "host" que conterá o SpriteVisual usando ElementCompositionPreview.SetElementChildVisual.

Visão geral da implementação

  1. Obter o folheto Visual para o elemento host
  2. Criar um DropShadow Windows.UI.Composition
  3. Configure o DropShadow para obter sua forma do elemento de destino por meio de uma máscara
    • DropShadow é retangular por padrão, portanto, isso não será necessário se o destino for retangular
  4. Anexe sombra a um novo SpriteVisual e defina o SpriteVisual como o filho do elemento host
  5. Associar o tamanho do SpriteVisual ao tamanho do host usando um 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);
}

As duas listagens a seguir mostram os equivalentes C++/WinRT e C++/CX do código C# anterior usando a mesma estrutura XAML.

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

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

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

    // Create a drop shadow
    auto dropShadow{ compositor.CreateDropShadow() };
    dropShadow.Color(Windows::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
    Windows::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);
}
#include "WindowsNumerics.h"

MainPage::MainPage()
{
    InitializeComponent();
    InitializeDropShadow(ShadowHost, CircleImage);
}

void MainPage::InitializeDropShadow(Windows::UI::Xaml::UIElement^ shadowHost, Windows::UI::Xaml::Shapes::Shape^ shadowTarget)
{
    auto hostVisual = Windows::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost);
    auto compositor = hostVisual->Compositor;

    // Create a drop shadow
    auto dropShadow = compositor->CreateDropShadow();
    dropShadow->Color = Windows::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
    Windows::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(shadowHost, shadowVisual);

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

    shadowVisual->StartAnimation("Size", bindSizeAnimation);
}

Vidro fosco

Crie um efeito que desfoque e tinga o conteúdo do plano de fundo. Observe que os desenvolvedores precisam instalar o pacote NuGet do Win2D para usar efeitos. Consulte a página inicial do Win2D para obter instruções de instalação.

Visão geral da implementação

  1. Obter folheto Visual para o elemento host
  2. Criar uma árvore de efeitos de desfoque usando Win2D e CompositionEffectSourceParameter
  3. Criar um CompositionEffectBrush com base na árvore de efeitos
  4. Defina a entrada do CompositionEffectBrush como um CompositionBackdropBrush, que permite que um efeito seja aplicado ao conteúdo por trás de um SpriteVisual
  5. Defina o CompositionEffectBrush como o conteúdo de um novo SpriteVisual e defina o SpriteVisual como o filho do elemento host. Você pode usar um XamlCompositionBrushBase como alternativa.
  6. Associar o tamanho do SpriteVisual ao tamanho do host usando um 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);
}

Recursos adicionais