Uso del livello visivo con XAML

La maggior parte delle app che usano le funzionalità del livello visivo userà XAML per definire il contenuto principale dell'interfaccia utente. Nell'aggiornamento dell'anniversario di Windows 10 sono disponibili nuove funzionalità nel framework XAML e nel livello visivo che semplificano la combinazione di queste due tecnologie per creare esperienze utente straordinarie. La funzionalità di interoperabilità XAML e del livello visivo può essere usata per creare animazioni ed effetti avanzati non disponibili usando unicamente le API XAML. Valuta gli ambiti seguenti:

  • Effetti pennello come sfocatura e vetro smerigliato
  • Effetti di illuminazione dinamica
  • Animazioni guidate da scorrimento e parallasse
  • Animazioni automatiche del layout
  • Ombreggiature perfette

Questi effetti e animazioni possono essere applicati al contenuto XAML esistente, quindi non è necessario ristrutturare drasticamente l'app XAML per sfruttare le nuove funzionalità. Le animazioni di layout, le ombreggiature e gli effetti sfocatura sono illustrati nella sezione Ricette di seguito. Per un esempio di codice che implementa parallasse, vedere l'esempio ParallaxingListItems. Il repository WindowsCompositionSamples include diversi altri esempi per l'implementazione di animazioni, ombreggiature ed effetti.

Classe XamlCompositionBrushBase

XamlCompositionBrush fornisce una classe di base per i pennelli XAML che disegnano un'area con compositionBrush. Questa opzione può essere usata per applicare facilmente effetti di composizione come sfocatura o vetro smerigliato agli elementi dell'interfaccia utente XAML.

Per altre informazioni sull'uso dei pennelli con l'interfaccia utente XAML, vedere la sezione Pennelli.

Per esempi di codice, vedi la pagina di riferimento per XamlCompositionBrushBase.

Classe XamlLight

XamlLight fornisce una classe di base per gli effetti di illuminazione XAML che illuminano dinamicamente un'area con CompositionLight.

Vedere la sezione Illuminazione per altre info sull'uso delle luci, inclusi gli elementi dell'interfaccia utente XAML per l'illuminazione.

Per esempi di codice, vedere la pagina di riferimento per XamlLight.

Classe ElementCompositionPreview

ElementCompositionPreview è una classe statica che fornisce funzionalità di interoperabilità XAML e Visual Layer. Per una panoramica del livello visivo e delle relative funzionalità, vedere Livello visivo. La classe ElementCompositionPreview fornisce i seguenti metodi:

  • GetElementVisual: ottiene un oggetto visivo "handout" usato per eseguire il rendering di questo elemento
  • SetElementChildVisual: imposta un oggetto visivo "handin" come ultimo elemento figlio della struttura ad albero visuale di questo elemento. Questo oggetto visivo verrà disegnato sopra il resto dell'elemento.
  • GetElementChildVisual: recupera il set di oggetti visivi usando SetElementChildVisual
  • GetScrollViewerManipulationPropertySet: ottiene un oggetto che può essere usato per creare animazioni di 60fps in base all'offset di scorrimento in ScrollViewer

Osservazioni su ElementCompositionPreview.GetElementVisual

ElementCompositionPreview.GetElementVisual restituisce un oggetto visivo "handout" usato per eseguire il rendering dell'oggetto UIElement specificato. Le proprietà come Visual.Opacity, Visual.Offset e Visual.Size vengono impostate dal framework XAML in base allo stato di UIElement. Ciò abilita tecniche come le animazioni di riposizionamento implicito (vedere Ricette).

Si noti che poiché offset e dimensioni sono impostati come risultato del layout del framework XAML, gli sviluppatori devono prestare attenzione quando si modificano o animano queste proprietà. Gli sviluppatori devono modificare o animare offset solo quando l'angolo superiore sinistro dell'elemento ha la stessa posizione di quella del relativo elemento padre nel layout. Le dimensioni in genere non devono essere modificate, ma l'accesso alla proprietà può essere utile. Ad esempio, gli esempi Ombreggiatura e Vetro smerigliato riportati di seguito usano dimensioni di un oggetto visivo di stampa come input per un'animazione.

Come avvertenza aggiuntiva, le proprietà aggiornate dell'oggetto visivo di stampa non verranno riflesse nell'oggetto UIElement corrispondente. Ad esempio, l'impostazione di UIElement.Opacity su 0.5 imposta l'opacità dell'oggetto visivo corrispondente su 0.5. Tuttavia, l'impostazione dell'Opacità dell'oggetto visivo dell'handout su 0,5 causerà la visualizzazione del contenuto con opacità al 50%, ma non modificherà il valore della proprietà Opacity di UIElement corrispondente.

Esempio di animazione Offset

Non corretto

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

Corretto

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

Metodo ElementCompositionPreview.SetElementChildVisual

ElementCompositionPreview.SetElementChildVisual consente allo sviluppatore di fornire un oggetto visivo "manuale" che verrà visualizzato come parte della struttura ad albero visuale di un elemento. Ciò consente agli sviluppatori di creare una sorta di "isola di composizione" in cui il contenuto basato su oggetti visivi può essere visualizzato all'interno di un'interfaccia utente XAML. Gli sviluppatori devono usare questa tecnica con parsimonia perché il contenuto basato su oggetti visivi non avrà le stesse garanzie di accessibilità ed esperienza utente del contenuto XAML. Pertanto, è generalmente consigliabile usare questa tecnica solo quando necessario per implementare effetti personalizzati, ad esempio quelli presenti nella sezione Ricette di seguito.

Metodi GetAlphaMask

Image, TextBlock e Shape implementano ogni metodo denominato GetAlphaMask che restituisce un oggetto CompositionBrush che rappresenta un'immagine in scala di grigi con la forma dell'elemento. Questo CompositionBrush può fungere da input per un DropShadow , in modo che l'ombreggiatura possa riflettere la forma dell'elemento invece di un rettangolo. In questo modo si creano ombreggiature perfette basate su contorno per testo, immagini con alfa e forme. Per un esempio di questa API, vedere Ombreggiatura di seguito.

Ricette

Animazione di riposizionamento

Usando le animazioni implicite di composizione, uno sviluppatore può animare automaticamente le modifiche nel layout di un elemento rispetto al relativo elemento padre. Ad esempio, se si modifica il margine del pulsante sottostante, l'animazione verrà automaticamente apportata alla nuova posizione del layout.

Panoramica dell'implementazione

  1. Ottenere l'oggetto visivo per l'elemento di destinazione
  2. Creare un oggetto ImplicitAnimationCollection che anima automaticamente le modifiche nella proprietà Offset
  3. Associare ImplicitAnimationCollection all'oggetto visivo di backup
<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;
}

Ombreggiatura

Applicare un'ombreggiatura perfetta a un UIElement, ad esempio un'ellisse contenente un'immagine. Poiché l'ombreggiatura richiede uno SpriteVisual creato dall'app, è necessario creare un elemento "host" che conterrà SpriteVisual usando ElementCompositionPreview.SetElementChildVisual.

Panoramica dell'implementazione

  1. Ottenere l'oggetto visivo per l'elemento host
  2. Creare un DropShadow di Windows.UI.Composition
  3. Configurare DropShadow per ottenere la forma dall'elemento di destinazione tramite una maschera
    • DropShadow è rettangolare per impostazione predefinita, pertanto questa azione non è necessaria se la destinazione è rettangolare
  4. Collegare l'ombreggiatura a un nuovo SpriteVisual e impostare SpriteVisual come figlio dell'elemento host
  5. Associare le dimensioni di SpriteVisual alle dimensioni dell'host usando 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);
}

I due elenchi seguenti mostrano gli equivalenti C++/WinRT e C++/CX del codice C# precedente usando la stessa struttura 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);
}

Vetro smerigliato

Creare un effetto che sfochi e colori il contenuto di sfondo. Si noti che gli sviluppatori devono installare il pacchetto NuGet Win2D per usare gli effetti. Vedere la pagina di Win2D per le istruzioni di installazione.

Panoramica dell'implementazione

  1. Ottenere l'oggetto visivo per l'elemento host
  2. Creare un albero degli effetti sfocatura usando Win2D e CompositionEffectSourceParameter
  3. Creare un oggetto CompositionEffectBrush in base all'albero degli effetti
  4. Impostare l'input di CompositionEffectBrush su compositionBackdropBrush, che consente di applicare un effetto al contenuto dietro un oggetto SpriteVisual
  5. Impostare CompositionEffectBrush come contenuto di un nuovo SpriteVisual e impostare SpriteVisual come figlio dell'elemento host. In alternativa, è possibile usare XamlCompositionBrushBase.
  6. Associare le dimensioni di SpriteVisual alle dimensioni dell'host usando 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);
}

Risorse aggiuntive