Introducción a las transformaciones

Obtenga información sobre cómo usar transformaciones en la API de Windows Runtime cambiando los sistemas de coordenadas relativos de los elementos de la interfaz de usuario. Esto se puede usar para ajustar la apariencia de elementos XAML individuales, como el escalado, la rotación o la transformación de la posición en el espacio x-y.

¿Qué es una transformación?

Una transformación define cómo asignar, o transformar, puntos de un espacio de coordenadas a otro espacio de coordenadas. Cuando se aplica una transformación a un elemento de interfaz de usuario, cambia cómo se representa ese elemento de interfaz de usuario en la pantalla como parte de la interfaz de usuario.

Piense en transformaciones en cuatro clasificaciones amplias: traducción, rotación, escalado y asimetría (o escalado). Para usar las API de gráficos para cambiar la apariencia de los elementos de la interfaz de usuario, suele ser más fácil crear transformaciones que definen solo una operación a la vez. Por lo tanto, Windows Runtime define una clase discreta para cada una de estas clasificaciones de transformación:

De estos, es probable que use TranslateTransform y ScaleTransform con más frecuencia para escenarios de interfaz de usuario.

Puedes combinar transformaciones y hay dos clases de Windows Runtime que admiten esto: CompositeTransform y TransformGroup. En compositeTransform, las transformaciones se aplican en este orden: escala, asimetría, rotación, traducción. Use TransformGroup en lugar de CompositeTransform si desea aplicar las transformaciones en un orden diferente. Para obtener más información, consulta CompositeTransform.

Transformaciones y diseño

En el diseño XAML, las transformaciones se aplican una vez completado el paso de diseño, por lo que se han tomado cálculos de espacio disponibles y otras decisiones de diseño antes de aplicar las transformaciones. Dado que el diseño se produce en primer lugar, a veces obtendrá resultados inesperados si transforma elementos que se encuentran en una celda Grid o en un contenedor de diseño similar que asigna espacio durante el diseño. El elemento transformado puede aparecer truncado o oculto porque intenta dibujar en un área que no calculó las dimensiones posteriores a la transformación al dividir el espacio dentro de su contenedor primario. Es posible que tenga que experimentar con los resultados de la transformación y ajustar algunas opciones de configuración. Por ejemplo, en lugar de confiar en el diseño adaptable y el ajuste de tamaño de estrella, es posible que tenga que cambiar las propiedades del Centro o declarar medidas de píxeles fijas para el espacio de diseño para asegurarse de que los espacios primarios tienen suficiente espacio.

Nota de migración: Windows Presentation Foundation (WPF) tenía una propiedad LayoutTransform que aplicaba transformaciones antes del paso de diseño. Pero XAML de Windows Runtime no admite una propiedad LayoutTransform . (Microsoft Silverlight tampoco tenía esta propiedad).

Como alternativa, el kit de herramientas de la Comunidad Windows proporciona el objeto LayoutTransformControl que aplica las transformaciones de matriz en cualquier objeto FrameworkElement de la aplicación.

Aplicación de una transformación a un elemento de la interfaz de usuario

Al aplicar una transformación a un objeto, normalmente lo hace para establecer la propiedad UIElement.RenderTransform. Establecer esta propiedad no cambia literalmente el píxel del objeto por píxel. Lo que realmente hace la propiedad es aplicar la transformación dentro del espacio de coordenadas local en el que existe ese objeto. A continuación, la lógica y la operación de representación (posterior al diseño) representan los espacios de coordenadas combinados, lo que hace que parezca que el objeto ha cambiado la apariencia y también potencialmente su posición de diseño (si se aplicó TranslateTransform).

De forma predeterminada, cada transformación de representación se centra en el origen del sistema de coordenadas local del objeto de destino( su (0,0). La única excepción es translateTransform, que no tiene propiedades central que establecer porque el efecto de traducción es el mismo independientemente de dónde se centre. Pero las otras transformaciones tienen propiedades que establecen los valores CenterX y CenterY .

Siempre que use transformaciones con UIElement.RenderTransform, recuerde que hay otra propiedad en UIElement que afecta al comportamiento de la transformación: RenderTransformOrigin. Lo que RenderTransformOrigin declara es si toda la transformación debe aplicarse al punto predeterminado (0,0) de un elemento o a algún otro punto de origen dentro del espacio de coordenadas relativo de ese elemento. Para los elementos típicos, (0,0) coloca la transformación en la esquina superior izquierda. Dependiendo del efecto que desee, puede optar por cambiar RenderTransformOrigin en lugar de ajustar los valores CenterX y CenterY en las transformaciones. Tenga en cuenta que si aplica los valores RenderTransformOrigin y CenterX / CenterY, los resultados pueden resultar bastante confusos, especialmente si anima cualquiera de los valores.

Con fines de pruebas de posicionamiento, un objeto al que se aplica una transformación continúa respondiendo a la entrada de una manera esperada que es coherente con su aspecto visual en el espacio x-y. Por ejemplo, si ha usado translateTransform para mover un rectángulo de 400 píxeles lateralmente en la interfaz de usuario, ese rectángulo responde a eventos PointerPressed cuando el usuario presiona el punto donde aparece visualmente el rectángulo. No obtendrá eventos falsos si el usuario presiona el área donde estaba el rectángulo antes de traducirse. Para cualquier consideración de índice z que afecte a las pruebas de posicionamiento, aplicar una transformación no hace ninguna diferencia; el índice z que rige qué elemento controla los eventos de entrada de un punto en el espacio x-y todavía se evalúa mediante el orden secundario como se declara en un contenedor. Ese orden suele ser el mismo que el orden en el que declaras los elementos en XAML, aunque para los elementos secundarios de un objeto Canvas puedes ajustar el orden aplicando la propiedad adjunta Canvas.ZIndex a elementos secundarios.

Otras propiedades de las transformaciones

Animación de una transformación

Los objetos Transform se pueden animar. Para animar una transformación, aplique una animación de un tipo compatible a la propiedad que desea animar. Normalmente, esto significa que usas objetos DoubleAnimation o DoubleAnimationUsingKeyFrames para definir la animación, ya que todas las propiedades de transformación son de tipo Double. Las animaciones que afectan a una transformación que se usa para un valor UIElement.RenderTransform no se consideran animaciones dependientes, incluso si tienen una duración distinta de cero. Para obtener más información sobre las animaciones dependientes, consulta Animaciones con guion gráfico.

Si anima propiedades para producir un efecto similar a una transformación en términos de apariencia visual net(por ejemplo, animando el ancho y alto de un FrameworkElement en lugar de aplicar un TranslateTransform), estas animaciones casi siempre se tratan como animaciones dependientes. Tendrías que habilitar las animaciones y podría haber problemas de rendimiento significativos con la animación, especialmente si intentas admitir la interacción del usuario mientras ese objeto se está animando. Por esa razón, es preferible usar una transformación y animarla en lugar de animar cualquier otra propiedad en la que la animación se trataría como una animación dependiente.

Para establecer como destino la transformación, debe haber una transformación existente como valor para RenderTransform. Normalmente, colocas un elemento para el tipo de transformación adecuado en el XAML inicial, a veces sin propiedades establecidas en esa transformación.

Normalmente se usa una técnica de segmentación indirecta para aplicar animaciones a las propiedades de una transformación. Para obtener más información sobre la sintaxis de destino indirecto, consulta Animaciones con guion gráfico y sintaxis de ruta de acceso de propiedades.

Los estilos predeterminados para los controles a veces definen animaciones de transformaciones como parte de su comportamiento de estado visual. Por ejemplo, los estados visuales de ProgressRing usan valores de RotateTransform animados para "girar" los puntos en el anillo.

Este es un ejemplo sencillo de cómo animar una transformación. En este caso, anima el ángulo de un RotateTransform para girar un rectángulo en su lugar alrededor de su centro visual. En este ejemplo se asigna un nombre a RotateTransform , por lo que no necesita destinos de animación indirectos, pero también podría dejar la transformación sin nombre, asignar un nombre al elemento al que se aplicó la transformación y usar destinos indirectos como (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();
}

Explicación de los marcos de coordenadas de referencia en el tiempo de ejecución

UIElement tiene un método denominado TransformToVisual, que puede generar una clase Transform que se correlaciona con los marcos de coordenadas de referencia de dos elementos de la interfaz de usuario. Puede usarlo para comparar un elemento con el marco de coordenada predeterminado de la aplicación si pasa el objeto visual raíz como primer parámetro. Esto puede ser útil si ha capturado un evento de entrada de un elemento diferente o si está intentando predecir el comportamiento de diseño sin solicitar realmente un pase de diseño.

Los datos de eventos obtenidos a partir de eventos de puntero proporcionan acceso a un método GetCurrentPoint, donde puede especificar un parámetro relativeTo para cambiar el marco de coordenada de referencia a un elemento específico en lugar del valor predeterminado de la aplicación. Este enfoque simplemente aplica una transformación de traducción internamente y transforma los datos de coordenadas x-y para usted cuando crea el objeto PointerPoint devuelto.

Descripción matemática de una transformación

Una transformación se puede describir en términos de una matriz de transformación. Se usa una matriz 3×3 para describir las transformaciones en un plano x-y bidimensional. Las matrices de transformación Affine se pueden multiplicar para formar cualquier número de transformaciones lineales, como rotación y asimetría (escote), seguidas de la traducción. La columna final de una matriz de transformación affine es igual a (0, 0, 1), por lo que solo debe especificar los miembros de las dos primeras columnas de la descripción matemática.

La descripción matemática de una transformación puede ser útil si tiene un fondo matemático o una familiaridad con las técnicas de programación de gráficos que también usan matrices para describir las transformaciones del espacio de coordenadas. Hay una clase derivada de transform que permite expresar una transformación directamente en términos de su matriz 3×3: MatrixTransform. MatrixTransform tiene una propiedad Matrix, que contiene una estructura que tiene seis propiedades: M11, M12, M21, M22, OffsetX y OffsetY. Cada propiedad Matrix usa un valor Double y corresponde a los seis valores pertinentes (columnas 1 y 2) de una matriz de transformación affine.

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

Cualquier transformación que pueda describir con un objeto TranslateTransform, ScaleTransform, RotateTransform o SkewTransform podría describirse igualmente por un objeto MatrixTransform con un valor Matrix. Pero normalmente solo usa TranslateTransform y los demás porque las propiedades de esas clases de transformación son más fáciles de conceptualizar que establecer los componentes vectoriales en una matriz. También es más fácil animar las propiedades discretas de las transformaciones; Una matriz es realmente una estructura y no un DependencyObject, por lo que no puede admitir valores individuales animados.

Algunas herramientas de diseño XAML que te permiten aplicar operaciones de transformación serializarán los resultados como MatrixTransform. En este caso, puede ser mejor volver a usar la misma herramienta de diseño para cambiar el efecto de transformación y serializar el XAML de nuevo, en lugar de intentar manipular los valores de Matriz directamente en el XAML.

Transformaciones 3D

En Windows 10, XAML introdujo una nueva propiedad, UIElement.Transform3D, que se puede usar para crear efectos 3D con la interfaz de usuario. Para ello, use PerspectiveTransform3D para agregar una perspectiva 3D compartida o "cámara" a la escena y, a continuación, use CompositeTransform3D para transformar un elemento en espacio 3D, como usaría CompositeTransform. Consulte UIElement.Transform3D para obtener una explicación sobre cómo implementar transformaciones 3D.

Para efectos 3D más sencillos que solo se aplican a un solo objeto, se puede usar la propiedad UIElement.Projection. El uso de planeProjection como valor para esta propiedad es equivalente a aplicar una transformación de perspectiva fija y una o varias transformaciones 3D al elemento. Este tipo de transformación se describe con más detalle en efectos de perspectiva 3D para la interfaz de usuario XAML.