Visuelle Kompositionselemente
Visuelle Kompositionselemente bilden die visuelle Struktur, die alle anderen Features der Kompositions-API verwenden und aufbauen. Mit der API können Entwickler ein oder mehrere visuelle Objekte definieren und erstellen, die jeweils einen einzelnen Knoten in einer visuellen Struktur darstellen.
Visuals
Es gibt mehrere visuelle Typen, aus denen die visuelle Struktur besteht, sowie eine Basispinselklasse mit mehreren Unterklassen, die sich auf den Inhalt eines visuellen Elements auswirken:
- Visual – Basisobjekt, der Großteil der Eigenschaften sind hier und von den anderen visuellen Objekten geerbt.
- ContainerVisual – abgeleitet von Visual und fügt die Möglichkeit zum Erstellen von untergeordneten Elementen hinzu.
- SpriteVisual – wird von ContainerVisual abgeleitet. Verfügt über die Möglichkeit, einen Pinsel zuzuordnen, sodass das Visuelle Pixel, einschließlich Bildern, Effekten oder einer Volltonfarbe, rendern kann.
- LayerVisual – abgeleitet von ContainerVisual. Untergeordnete Elemente des visuellen Elements werden in einer einzigen Ebene abgeflacht.
(Eingeführt in Windows 10, Version 1607, SDK 14393.) - ShapeVisual – abgeleitet von ContainerVisual. Ein visueller Strukturknoten, der der Stamm einer CompositionShape ist.
(Eingeführt in Windows 10, Version 1803, SDK 17134.) - RedirectVisual – abgeleitet von ContainerVisual. Das Visuelle ruft seinen Inhalt aus einem anderen visuellen Element ab.
(Eingeführt in Windows 10, Version 1809, SDK 17763.) - SceneVisual – abgeleitet von ContainerVisual. Ein containervisual für die Knoten einer 3D-Szene.
(Eingeführt in Windows 10, Version 1903, SDK 18362.)
Sie können Inhalte und Effekte auf SpriteVisuals mithilfe von CompositionBrush und deren Unterklassen anwenden, einschließlich compositionColorBrush, CompositionSurfaceBrush und CompositionEffectBrush. Weitere Informationen zu Pinsel finden Sie unter CompositionBrush Overview.
Das CompositionVisual-Beispiel
Hier sehen wir uns einen Beispielcode an, der die drei verschiedenen zuvor aufgeführten visuellen Typen veranschaulicht. Obwohl dieses Beispiel keine Konzepte wie Animationen oder komplexere Effekte behandelt, enthält es die Bausteine, die alle diese Systeme verwenden. (Der vollständige Beispielcode wird am Ende dieses Artikels aufgeführt.)
Im Beispiel handelt es sich um eine Reihe von Volltonfarbquaden, auf die geklickt und über den Bildschirm gezogen werden kann. Wenn auf ein Quadrat geklickt wird, kommt es nach vorne, dreht sich um 45 Grad und wird undurchsichtig, wenn es gezogen wird.
Dies zeigt eine Reihe grundlegender Konzepte für die Arbeit mit der API, einschließlich:
- Erstellen eines Kompositors
- Erstellen eines SpriteVisual-Elements mit einem CompositionColorBrush
- Beschneiden des visuellen Elements
- Drehen des visuellen Elements
- Festlegen der Deckkraft
- Ändern der Position des visuellen Elements in der Auflistung.
Erstellen eines Kompositors
Das Erstellen eines Kompositors und das Speichern in einer Variablen für die Verwendung als Factory ist eine einfache Aufgabe. Der folgende Codeausschnitt zeigt das Erstellen eines neuen Kompositors:
_compositor = new Compositor();
Erstellen eines SpriteVisual- und ColorBrush-Elements
Die Verwendung des Kompositors ist es einfach, Objekte immer dann zu erstellen, wenn Sie sie benötigen, z. B. ein SpriteVisual und ein CompositionColorBrush:
var visual = _compositor.CreateSpriteVisual();
visual.Brush = _compositor.CreateColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
Dies ist zwar nur ein paar Codezeilen, veranschaulicht aber ein leistungsfähiges Konzept: SpriteVisual-Objekte sind das Herzstück des Effektsystems. Das SpriteVisual ermöglicht eine hohe Flexibilität und Interaktion bei der Farb-, Bild- und Effekterstellung. SpriteVisual ist ein einzelner visueller Typ, der ein 2D-Rechteck mit einem Pinsel füllen kann, in diesem Fall eine Volltonfarbe.
Beschneiden eines visuellen Elements
Der Kompositor kann auch zum Erstellen von Clips für ein visuelles Element verwendet werden. Im Folgenden finden Sie ein Beispiel aus dem Beispiel für die Verwendung von InsetClip , um jede Seite des visuellen Elements zu kürzen:
var clip = _compositor.CreateInsetClip();
clip.LeftInset = 1.0f;
clip.RightInset = 1.0f;
clip.TopInset = 1.0f;
clip.BottomInset = 1.0f;
_currentVisual.Clip = clip;
Wie andere Objekte in der API kann InsetClip Animationen auf seine Eigenschaften anwenden.
Drehen eines Clips
Ein visuelles Element kann mit einer Drehung transformiert werden. Beachten Sie, dass RotationAngle sowohl Bogenmaß als auch Grad unterstützt. Standardmäßig wird Bogenmaß verwendet, aber es ist einfach, Grad anzugeben, wie im folgenden Codeausschnitt gezeigt:
child.RotationAngleInDegrees = 45.0f;
Drehung ist nur ein Beispiel für eine Reihe von Transformationskomponenten, die von der API bereitgestellt werden, um diese Aufgaben zu vereinfachen. Andere umfassen Offset, Scale, Orientation, RotationAxis und 4x4 TransformMatrix.
Festlegen der Deckkraft
Das Festlegen der Deckkraft eines visuellen Elements ist ein einfacher Vorgang mit einem Float-Wert. In der Stichprobe beginnen beispielsweise alle Quadrate bei 0,8 Deckkraft:
visual.Opacity = 0.8f;
Wie die Drehung kann die Opacity-Eigenschaft animiert werden.
Ändern der Position des visuellen Elements in der Auflistung
Die Kompositions-API ermöglicht es, die Position eines visuellen Elements in einer VisualCollection auf verschiedene Arten zu ändern. Es kann über einem anderen Visual mit InsertAbove platziert werden, darunter mit InsertBelow, nach oben mit InsertAtTop oder unten mit InsertAtBottom verschoben werden.
Im Beispiel wird oben ein durch klickenes Visuelles Element sortiert:
parent.Children.InsertAtTop(_currentVisual);
Vollständiges Beispiel
Im vollständigen Beispiel werden alle oben genannten Konzepte zusammen verwendet, um eine einfache Struktur von visuellen Objekten zu erstellen und zu durchlaufen, um die Deckkraft zu ändern, ohne XAML, WWA oder DirectX zu verwenden. In diesem Beispiel wird gezeigt, wie untergeordnete visuelle Objekte erstellt und hinzugefügt und wie Eigenschaften geändert werden.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Core;
namespace compositionvisual
{
class VisualProperties : IFrameworkView
{
//------------------------------------------------------------------------------
//
// VisualProperties.Initialize
//
// This method is called during startup to associate the IFrameworkView with the
// CoreApplicationView.
//
//------------------------------------------------------------------------------
void IFrameworkView.Initialize(CoreApplicationView view)
{
_view = view;
_random = new Random();
}
//------------------------------------------------------------------------------
//
// VisualProperties.SetWindow
//
// This method is called when the CoreApplication has created a new CoreWindow,
// allowing the application to configure the window and start producing content
// to display.
//
//------------------------------------------------------------------------------
void IFrameworkView.SetWindow(CoreWindow window)
{
_window = window;
InitNewComposition();
_window.PointerPressed += OnPointerPressed;
_window.PointerMoved += OnPointerMoved;
_window.PointerReleased += OnPointerReleased;
}
//------------------------------------------------------------------------------
//
// VisualProperties.OnPointerPressed
//
// This method is called when the user touches the screen, taps it with a stylus
// or clicks the mouse.
//
//------------------------------------------------------------------------------
void OnPointerPressed(CoreWindow window, PointerEventArgs args)
{
Point position = args.CurrentPoint.Position;
//
// Walk our list of visuals to determine who, if anybody, was selected
//
foreach (var child in _root.Children)
{
//
// Did we hit this child?
//
Vector3 offset = child.Offset;
Vector2 size = child.Size;
if ((position.X >= offset.X) &&
(position.X < offset.X + size.X) &&
(position.Y >= offset.Y) &&
(position.Y < offset.Y + size.Y))
{
//
// This child was hit. Since the children are stored back to front,
// the last one hit is the front-most one so it wins
//
_currentVisual = child as ContainerVisual;
_offsetBias = new Vector2((float)(offset.X - position.X),
(float)(offset.Y - position.Y));
}
}
//
// If a visual was hit, bring it to the front of the Z order
//
if (_currentVisual != null)
{
ContainerVisual parent = _currentVisual.Parent as ContainerVisual;
parent.Children.Remove(_currentVisual);
parent.Children.InsertAtTop(_currentVisual);
}
}
//------------------------------------------------------------------------------
//
// VisualProperties.OnPointerMoved
//
// This method is called when the user moves their finger, stylus or mouse with
// a button pressed over the screen.
//
//------------------------------------------------------------------------------
void OnPointerMoved(CoreWindow window, PointerEventArgs args)
{
//
// If a visual is selected, drag it with the pointer position and
// make it opaque while we drag it
//
if (_currentVisual != null)
{
//
// Set up the properties of the visual the first time it is
// dragged. This will last for the duration of the drag
//
if (!_dragging)
{
_currentVisual.Opacity = 1.0f;
//
// Transform the first child of the current visual so that
// the image is rotated
//
foreach (var child in _currentVisual.Children)
{
child.RotationAngleInDegrees = 45.0f;
child.CenterPoint = new Vector3(_currentVisual.Size.X / 2, _currentVisual.Size.Y / 2, 0);
break;
}
//
// Clip the visual to its original layout rect by using an inset
// clip with a one-pixel margin all around
//
var clip = _compositor.CreateInsetClip();
clip.LeftInset = 1.0f;
clip.RightInset = 1.0f;
clip.TopInset = 1.0f;
clip.BottomInset = 1.0f;
_currentVisual.Clip = clip;
_dragging = true;
}
Point position = args.CurrentPoint.Position;
_currentVisual.Offset = new Vector3((float)(position.X + _offsetBias.X),
(float)(position.Y + _offsetBias.Y),
0.0f);
}
}
//------------------------------------------------------------------------------
//
// VisualProperties.OnPointerReleased
//
// This method is called when the user lifts their finger or stylus from the
// screen, or lifts the mouse button.
//
//------------------------------------------------------------------------------
void OnPointerReleased(CoreWindow window, PointerEventArgs args)
{
//
// If a visual was selected, make it transparent again when it is
// released and restore the transform and clip
//
if (_currentVisual != null)
{
if (_dragging)
{
//
// Remove the transform from the first child
//
foreach (var child in _currentVisual.Children)
{
child.RotationAngle = 0.0f;
child.CenterPoint = new Vector3(0.0f, 0.0f, 0.0f);
break;
}
_currentVisual.Opacity = 0.8f;
_currentVisual.Clip = null;
_dragging = false;
}
_currentVisual = null;
}
}
//------------------------------------------------------------------------------
//
// VisualProperties.Load
//
// This method is called when a specific page is being loaded in the
// application. It is not used for this application.
//
//------------------------------------------------------------------------------
void IFrameworkView.Load(string unused)
{
}
//------------------------------------------------------------------------------
//
// VisualProperties.Run
//
// This method is called by CoreApplication.Run() to actually run the
// dispatcher's message pump.
//
//------------------------------------------------------------------------------
void IFrameworkView.Run()
{
_window.Activate();
_window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
}
//------------------------------------------------------------------------------
//
// VisualProperties.Uninitialize
//
// This method is called during shutdown to disconnect the CoreApplicationView,
// and CoreWindow from the IFrameworkView.
//
//------------------------------------------------------------------------------
void IFrameworkView.Uninitialize()
{
_window = null;
_view = null;
}
//------------------------------------------------------------------------------
//
// VisualProperties.InitNewComposition
//
// This method is called by SetWindow(), where we initialize Composition after
// the CoreWindow has been created.
//
//------------------------------------------------------------------------------
void InitNewComposition()
{
//
// Set up Windows.UI.Composition Compositor, root ContainerVisual, and associate with
// the CoreWindow.
//
_compositor = new Compositor();
_root = _compositor.CreateContainerVisual();
_compositionTarget = _compositor.CreateTargetForCurrentView();
_compositionTarget.Root = _root;
//
// Create a few visuals for our window
//
for (int index = 0; index < 20; index++)
{
_root.Children.InsertAtTop(CreateChildElement());
}
}
//------------------------------------------------------------------------------
//
// VisualProperties.CreateChildElement
//
// Creates a small sub-tree to represent a visible element in our application.
//
//------------------------------------------------------------------------------
Visual CreateChildElement()
{
//
// Each element consists of three visuals, which produce the appearance
// of a framed rectangle
//
var element = _compositor.CreateContainerVisual();
element.Size = new Vector2(100.0f, 100.0f);
//
// Position this visual randomly within our window
//
element.Offset = new Vector3((float)(_random.NextDouble() * 400), (float)(_random.NextDouble() * 400), 0.0f);
//
// The outer rectangle is always white
//
//Note to preview API users - SpriteVisual and Color Brush replace SolidColorVisual
//for example instead of doing
//var visual = _compositor.CreateSolidColorVisual() and
//visual.Color = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
//we now use the below
var visual = _compositor.CreateSpriteVisual();
element.Children.InsertAtTop(visual);
visual.Brush = _compositor.CreateColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
visual.Size = new Vector2(100.0f, 100.0f);
//
// The inner rectangle is inset from the outer by three pixels all around
//
var child = _compositor.CreateSpriteVisual();
visual.Children.InsertAtTop(child);
child.Offset = new Vector3(3.0f, 3.0f, 0.0f);
child.Size = new Vector2(94.0f, 94.0f);
//
// Pick a random color for every rectangle
//
byte red = (byte)(0xFF * (0.2f + (_random.NextDouble() / 0.8f)));
byte green = (byte)(0xFF * (0.2f + (_random.NextDouble() / 0.8f)));
byte blue = (byte)(0xFF * (0.2f + (_random.NextDouble() / 0.8f)));
child.Brush = _compositor.CreateColorBrush(Color.FromArgb(0xFF, red, green, blue));
//
// Make the subtree root visual partially transparent. This will cause each visual in the subtree
// to render partially transparent, since a visual's opacity is multiplied with its parent's
// opacity
//
element.Opacity = 0.8f;
return element;
}
// CoreWindow / CoreApplicationView
private CoreWindow _window;
private CoreApplicationView _view;
// Windows.UI.Composition
private Compositor _compositor;
private CompositionTarget _compositionTarget;
private ContainerVisual _root;
private ContainerVisual _currentVisual;
private Vector2 _offsetBias;
private bool _dragging;
// Helpers
private Random _random;
}
public sealed class VisualPropertiesFactory : IFrameworkViewSource
{
//------------------------------------------------------------------------------
//
// VisualPropertiesFactory.CreateView
//
// This method is called by CoreApplication to provide a new IFrameworkView for
// a CoreWindow that is being created.
//
//------------------------------------------------------------------------------
IFrameworkView IFrameworkViewSource.CreateView()
{
return new VisualProperties();
}
//------------------------------------------------------------------------------
//
// main
//
//------------------------------------------------------------------------------
static int Main(string[] args)
{
CoreApplication.Run(new VisualPropertiesFactory());
return 0;
}
}
}