편집

다음을 통해 공유


Transforms overview

Learn how to use transforms in the Windows Runtime API, by changing the relative coordinate systems of elements in the UI. This can be used to adjust the appearance of individual XAML elements, such as scaling, rotating, or transforming the position in x-y space.

What is a transform?

A transform defines how to map, or transform, points from one coordinate space to another coordinate space. When a transform is applied to a UI element, it changes how that UI element is rendered to the screen as part of the UI.

Think of transforms in four broad classifications: translation, rotation, scaling and skew (or shear). For the purposes of using graphics APIs to change the appearance of UI elements, it's usually easiest to create transforms that define only one operation at a time. So the Windows Runtime defines a discrete class for each of these transform classifications:

Of these, you're likely to use TranslateTransform and ScaleTransform most often for UI scenarios.

You can combine transforms, and there are two Windows Runtime classes that support this: CompositeTransform and TransformGroup. In a CompositeTransform, transforms are applied in this order: scale, skew, rotate, translate. Use TransformGroup instead of CompositeTransform if you want the transforms applied in a different order. For more info, see CompositeTransform.

Transforms and layout

In XAML layout, transforms are applied after the layout pass is complete, so available space calculations and other layout decisions have been made before the transforms are applied. Because layout comes first, you'll sometimes get unexpected results if you transform elements that are in a Grid cell or similar layout container that allocates space during layout. The transformed element may appear truncated or obscured because it's trying to draw into an area that didn't calculate the post-transform dimensions when dividing space within its parent container. You may need to experiment with the transform results and adjust some settings. For example, instead of relying on adaptive layout and star sizing, you may need to change the Center properties or declare fixed pixel measurements for layout space to make sure the parent allots enough space.

Migration note: Windows Presentation Foundation (WPF) had a LayoutTransform property that applied transforms prior to the layout pass. But Windows Runtime XAML doesn't support a LayoutTransform property. (Microsoft Silverlight didn't have this property either.)

As an alternative, the Windows Community Toolkit provides the LayoutTransformControl that applies Matrix transformations on any FrameworkElement of your application.

Applying a transform to a UI element

When you apply a transform to an object, you typically do so to set the property UIElement.RenderTransform. Setting this property does not literally change the object pixel by pixel. What the property really does is apply the transform within the local coordinate space in which that object exists. Then the rendering logic and operation (post-layout) renders the combined coordinate spaces, making it look like the object has changed appearance and also potentially its layout position (if TranslateTransform was applied).

By default, each render transform is centered at the origin of the target object's local coordinate system—its (0,0). The only exception is a TranslateTransform, which has no center properties to set because the translation effect is the same regardless of where it is centered. But the other transforms each have properties that set CenterX and CenterY values.

Whenever you use transforms with UIElement.RenderTransform, remember that there's another property on UIElement that affects how the transform behaves: RenderTransformOrigin. What RenderTransformOrigin declares is whether the whole transform should apply to the default (0,0) point of an element or to some other origin point within the relative coordinate space of that element. For typical elements, (0,0) places the transform to the top left corner. Depending on what effect you want, you might choose to change RenderTransformOrigin rather than adjusting the CenterX and CenterY values on transforms. Note that if you apply both RenderTransformOrigin and CenterX / CenterY values, the results can be pretty confusing, especially if you're animating any of the values.

For hit-testing purposes, an object to which a transform is applied continues to respond to input in an expected way that's consistent to its visual appearance in x-y space. For example, if you've used a TranslateTransform to move a Rectangle 400 pixels laterally in the UI, that Rectangle responds to PointerPressed events when the user presses the point where the Rectangle appears visually. You won't get false events if the user presses the area where the Rectangle was before being translated. For any z-index considerations that affect hit testing, applying a transform makes no difference; the z-index that governs which element handles input events for a point in x-y space is still evaluated using the child order as declared in a container. That order is usually the same as the order in which you declare the elements in XAML, although for child elements of a Canvas object you can adjust the order by applying the Canvas.ZIndex attached property to child elements.

Other transform properties

Animating a transform

Transform objects can be animated. To animate a Transform, apply an animation of a compatible type to the property you want to animate. This typically means you're using DoubleAnimation or DoubleAnimationUsingKeyFrames objects to define the animation, because all of the transform properties are of type Double. Animations that affect a transform that's used for a UIElement.RenderTransform value are not considered to be dependent animations, even if they have a nonzero duration. For more info about dependent animations, see Storyboarded animations.

If you animate properties to produce an effect similar to a transform in terms of the net visual appearance—for example, animating the Width and Height of a FrameworkElement rather than applying a TranslateTransform—such animations are almost always treated as dependent animations. You'd have to enable the animations and there could be significant performance issues with the animation, especially if you're trying to support user interaction while that object is being animated. For that reason it's preferable to use a transform and animate it rather than animating any other property where the animation would be treated as a dependent animation.

To target the transform, there must be an existing Transform as the value for RenderTransform. You typically put an element for the appropriate transform type in the initial XAML, sometimes with no properties set on that transform.

You typically use an indirect targeting technique to apply animations to the properties of a transform. For more info about indirect targeting syntax, see Storyboarded animations and Property-path syntax.

Default styles for controls sometimes define animations of transforms as part of their visual-state behavior. For example, the visual states for ProgressRing use animated RotateTransform values to "spin" the dots in the ring.

Here's a simple example of how to animate a transform. In this case, it's animating the Angle of a RotateTransform to spin a Rectangle in place around its visual center. This example names the RotateTransform so doesn't need indirect animation targeting, but you could alternatively leave the transform unnamed, name the element that the transform's applied to, and use indirect targeting such as (UIElement.RenderTransform).(RotateTransform.Angle).

<StackPanel Margin="15">
  <StackPanel.Resources>
    <Storyboard x:Name="myStoryboard">
      <DoubleAnimation
       Storyboard.TargetName="myTransform"
       Storyboard.TargetProperty="Angle"
       From="0" To="360" Duration="0:0:5" 
       RepeatBehavior="Forever" />
    </Storyboard>
  </StackPanel.Resources>
  <Rectangle Width="50" Height="50" Fill="RoyalBlue"
   PointerPressed="StartAnimation">
    <Rectangle.RenderTransform>
      <RotateTransform x:Name="myTransform" Angle="45" CenterX="25" CenterY="25" />
    </Rectangle.RenderTransform>
  </Rectangle>
</StackPanel>
void StartAnimation (object sender, RoutedEventArgs e) {
    myStoryboard.Begin();
}

Accounting for coordinate frames of reference at run time

UIElement has a method named TransformToVisual, which can generate a Transform that correlates the coordinate frames of reference for two UI elements. You can use this to compare an element to the app's default coordinate frame of reference if you pass the root visual as the first parameter. This can be useful if you've captured an input event from a different element, or if you are trying to predict layout behavior without actually requesting a layout pass.

Event data obtained from pointer events provides access to a GetCurrentPoint method, where you can specify a relativeTo parameter to change the coordinate frame of reference to a specific element rather than the app default. This approach simply applies a translate transform internally and transforms the x-y coordinate data for you when it creates the returned PointerPoint object.

Describing a transform mathematically

A transform can be described in terms of a transformation matrix. A 3×3 matrix is used to describe the transformations in a two-dimensional, x-y plane. Affine transformation matrices can be multiplied to form any number of linear transformations, such as rotation and skew (shear), followed by translation. The final column of an affine transformation matrix is equal to (0, 0, 1), so you need to specify only the members of the first two columns in the mathematical description.

The mathematical description of a transform might be useful to you if you have a mathematical background or a familiarity with graphics-programming techniques that also use matrices to describe transformations of coordinate space. There's a Transform-derived class that enables you to express a transformation directly in terms of its 3×3 matrix: MatrixTransform. MatrixTransform has a Matrix property, which holds a structure that has six properties: M11, M12, M21, M22, OffsetX and OffsetY. Each Matrix property uses a Double value and corresponds to the six relevant values (columns 1 and 2) of an affine transformation matrix.

Column 1 Column 2 Column 3
M11 M12 0
M21 M22 0
OffsetX OffsetY 1

Any transform that you could describe with a TranslateTransform, ScaleTransform, RotateTransform, or SkewTransform object could be described equally by a MatrixTransform with a Matrix value. But you typically just use TranslateTransform and the others because the properties on those transform classes are easier to conceptualize than setting the vector components in a Matrix. It's also easier to animate the discrete properties of transforms; a Matrix is actually a structure and not a DependencyObject, so it can't support animated individual values.

Some XAML design tools that enable you to apply transformation operations will serialize the results as a MatrixTransform. In this case it may be best to use the same design tool again to change the transformation effect and serialize the XAML again, rather than trying to manipulate the Matrix values yourself directly in the XAML.

3-D transforms

In Windows 10, XAML introduced a new property, UIElement.Transform3D, that can be used to create 3D effects with UI. To do this, use PerspectiveTransform3D to add a shared 3D perspective or "camera" to your scene, and then use CompositeTransform3D to transform an element in 3D space, like you would use CompositeTransform. See UIElement.Transform3D for a discussion of how to implement 3D transforms.

For simpler 3D effects that only apply to a single object, the UIElement.Projection property can be used. Using a PlaneProjection as the value for this property is equivalent to applying a fixed perspective transform and one or more 3D transforms to the element. This type of transform is described in more detail in 3-D perspective effects for XAML UI.