Verwenden der visuellen Ebene mit WinUI-XAML

Die meisten WinUI- und Windows App SDK-Apps, die Visual Layer-Funktionen nutzen, verwenden XAML, um den Hauptinhalt der Benutzeroberfläche zu definieren. WinUI bietet Features im XAML-Framework und der visuellen Ebene, die es einfacher machen, diese beiden Technologien zu kombinieren, um beeindruckende Benutzeroberflächen zu schaffen. Xaml- und Visual Layer-Interoperabilitätsfunktionen können verwendet werden, um erweiterte Animationen und Effekte zu erstellen, die nicht allein mit XAML-APIs verfügbar sind. Dazu gehören:

  • Pinseleffekte wie Unschärfe und Milchglas
  • Dynamische Beleuchtungseffekte
  • Bildlaufgesteuerte Animationen und Parallax-Effekte
  • Automatische Layoutanimationen
  • Pixelgenaue Schlagschatten

Diese Effekte und Animationen können auf vorhandene XAML-Inhalte angewendet werden, sodass Sie Ihre WinUI-App nicht erheblich umstrukturieren müssen, um die Funktionalität zu nutzen. Layout-Animationen, Schatten und Unschärfeeffekte werden im Abschnitt „Rezepte“ weiter unten behandelt. Ein Codebeispiel zur Implementierung von Parallax finden Sie im ParallaxingListItems-Beispiel. Die beispiele für Windows App SDK SceneGraph enthalten auch Beispiele für Animationen, Schatten und Effekte.

Die XamlCompositionBrushBase-Klasse

Microsoft.UI.Xaml.Media.XamlCompositionBrushBase stellt eine Basisklasse für XAML-Pinsel bereit, die einen Bereich mit einem CompositionBrush zeichnen. Dies kann dazu verwendet werden, Zusammensetzungseffekte wie Unschärfe oder Frostglas einfach auf XAML-UI-Elemente anzuwenden.

Weitere Informationen zur Verwendung von Pinseln mit XAML-UI finden Sie im Abschnitt "Brushes ".

Codebeispiele finden Sie auf der Referenzseite für XamlCompositionBrushBase.

Die XamlLight-Klasse

Microsoft.UI.Xaml.Media.XamlLight stellt eine Basisklasse für XAML-Beleuchtungseffekte bereit, die einen Bereich mit einem CompositionLight dynamisch beleuchten.

Weitere Informationen zur Verwendung von Lichtern, einschließlich XAML-UI-Elementen für beleuchtung, finden Sie im Abschnitt "Beleuchtung ".

Codebeispiele finden Sie auf der Referenzseite für XamlLight.

Arbeiten mit WinUI-XAML

ElementCompositionPreview ist eine statische Klasse, die XAML- und Visual Layer-Interoperabilitätsfunktionen bereitstellt. Eine Übersicht über die visuelle Ebene und deren Funktionalität finden Sie unter Visual Layer. Die ElementCompositionPreview-Klasse stellt die folgenden WinUI-Interopmethoden bereit:

  • GetElementVisual: Ruft ein Handout-Visual ab, das zum Rendern dieses Elements verwendet wird
  • SetElementChildVisual: Legt ein Handin-Visual als letztes untergeordnetes Element des visuellen Baums dieses Elements fest. Dieses Visual wird über den Rest des Elements dargestellt.
  • GetElementChildVisual: Abrufen des visuellen Satzes mithilfe von SetElementChildVisual
  • GetScrollViewerManipulationPropertySet: Abrufen eines Objekts, das zum Erstellen von 60fps-Animationen basierend auf dem Bildlaufoffset in einem ScrollViewer verwendet werden kann

GetElementVisual

ElementCompositionPreview.GetElementVisual gibt ein Handout-Visual zurück, das zum Rendern des angegebenen UIElement verwendet wird. Eigenschaften wie Visual.Opacity, Visual.Offset und Visual.Size werden vom XAML-Framework basierend auf dem Zustand des UIElements festgelegt. Dies ermöglicht Techniken wie implizite Neupositionierungsanimationen (siehe Rezepte).

Da Offset und Größe als Ergebnis des XAML-Frameworklayouts festgelegt werden, sollten Entwickler beim Ändern oder Animieren dieser Eigenschaften vorsichtig sein. Entwickler sollten den Offset nur ändern oder animieren, wenn die obere linke Ecke des Elements dieselbe Position wie die des übergeordneten Elements im Layout aufweist. Die Größe sollte im Allgemeinen nicht geändert werden, doch der Zugriff auf die Eigenschaft kann nützlich sein. Beispielsweise verwenden die unten aufgeführten Beispiele „Drop Shadow“ und „Frosted Glass“ die Größe eines Handout-Visuals als Eingabe für eine Animation.

Als weiterer Hinweis sei angemerkt, dass aktualisierte Eigenschaften des Handout-Visuals nicht im entsprechenden UIElement berücksichtigt werden. So legt beispielsweise die Einstellung von UIElement.Opacity auf 0,5 die Opacity des entsprechenden Handout-Visuals auf 0,5 fest. Wird jedoch die Opacity des Handout-Visuals auf 0,5 festgelegt, wird der Inhalt mit einer Deckkraft von 50 % angezeigt, der Wert der Opacity-Eigenschaft des entsprechenden UIElements ändert sich jedoch nicht.

Beispiel für Offsetanimation

Unrichtig

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

Richtig

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

SetElementChildVisual

ElementCompositionPreview.SetElementChildVisual erlaubt es dem Entwickler, ein Handin-Visual anzugeben, das als Teil des Visual-Baums eines Elements angezeigt wird. Auf diese Weise können Entwickler eine "Composition Island" erstellen, in der visuelle Inhalte in einer XAML-Benutzeroberfläche angezeigt werden können. Entwickler sollten bei der Verwendung dieser Technik vorsichtig sein, da visuelle Inhalte nicht über die gleichen Barrierefreiheits- und Benutzerfreundlichkeitsgarantien für XAML-Inhalte verfügen. Daher wird im Allgemeinen empfohlen, diese Technik nur bei Bedarf zum Implementieren von benutzerdefinierten Effekten wie denen im Abschnitt "Rezepte" unten zu verwenden.

GetAlphaMask-Methoden

Image, TextBlock und Shape implementieren jeweils eine Methode namens "GetAlphaMask ", die einen CompositionBrush zurückgibt, der ein Graustufenbild mit der Form des Elements darstellt. Dieser CompositionBrush kann als Eingabe für ein Composition DropShadow dienen, sodass der Schatten die Form des Elements anstelle eines Rechtecks widerspiegeln kann. Dies ermöglicht pixelgenaue, konturbasierte Schatten für Text, Bilder mit Alpha und Formen. Ein Beispiel für diese API finden Sie unter Drop Shadow weiter unten.

Rezepte

Animation neu anordnen

Mithilfe impliziter Kompositionsanimationen kann ein Entwickler änderungen im Layout eines Elements automatisch relativ zum übergeordneten Element animieren. Wenn Sie beispielsweise den Rand der schaltfläche unten ändern, wird sie automatisch an die neue Layoutposition animiert.

Übersicht zur Umsetzung

  1. Laden Sie das Handbuch Visual für das Zielelement herunter.
  2. Erstellen einer ImplicitAnimationCollection , die Änderungen in der Offset-Eigenschaft automatisch animiert
  3. Verknüpfung der ImplicitAnimationCollection mit dem zugrunde liegenden Visual
<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;
}

Schlagschatten

Wenden Sie einen pixelgenauen Schlagschatten auf ein UIElement an, z. B. eine Ellipse , die ein Bild enthält. Da für den Schatten ein von der App erstelltes SpriteVisual erforderlich ist, müssen wir ein "host"-Element erstellen, das das SpriteVisual mit ElementCompositionPreview.SetElementChildVisual enthält.

Übersicht zur Umsetzung

  1. Handin-Visual für das Host-Element abrufen
  2. Microsoft.UI.Composition DropShadow erstellen
  3. Konfigurieren Sie die DropShadow, damit seine Form über eine Maske vom Zielelement übernommen wird.
    • DropShadow ist standardmäßig rechteckig, daher ist dies nicht erforderlich, wenn das Ziel rechteckig ist.
  4. Schatten an ein neues SpriteVisual zuordnen und das SpriteVisual als untergeordnetes Element des Host-Elements festlegen
  5. Größe des SpriteVisual mit der Größe des Hosts mittels einer ExpressionAnimation binden
<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);
}

Die folgende Auflistung zeigt die C++/WinRT-Entsprechung des vorherigen C#-Codes mit derselben XAML-Struktur.

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

Milchglas

Erstellen Sie einen Effekt, der Hintergrundinhalte weichzeichnet und färbt. Beachten Sie, dass Entwickler das Win2D NuGet-Paket installieren müssen, um Effekte zu verwenden. Installationsanweisungen finden Sie auf der Win2D-Homepage .

Übersicht zur Umsetzung

  1. Handin-Visual für das Host-Element abrufen
  2. Blur-Effektbaum mithilfe von Win2D und CompositionEffectSourceParameter erstellen
  3. Erstellen Sie einen CompositionEffectBrush basierend auf dem Effektbaum
  4. Eingabeelement des CompositionEffectBrush auf einen CompositionBackdropBrush festlegen, wodurch es erlaubt ist, einen Effekt auf den Inhalt hinter einem SpriteVisual anzuwenden
  5. Legen Sie den CompositionEffectBrush als Inhalt eines neuen SpriteVisual fest und legen Sie das SpriteVisual als untergeordnetes Element des Host-Elements fest. Alternativ können Sie eine XamlCompositionBrushBase verwenden.
  6. Größe des SpriteVisual mit der Größe des Hosts mittels einer ExpressionAnimation binden
<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);
}

Zusätzliche Ressourcen