Visão geral das transformações

Saiba como usar as transformações na API do Windows Runtime alterando o sistema de coordenadas relativas de elementos na interface do usuário. Use esse método para ajustar a aparência de elementos XAML individuais, como dimensionar, girar ou transformar a posição no espaço x-y.

O que é uma transformação?

Uma transformação define como mapear, ou transformar, pontos de um espaço de coordenadas em outro espaço de coordenadas. Quando uma transformação é aplicada a um elemento da interface do usuário, ela muda como esse elemento da interface do usuário é renderizado para a tela como parte da interface do usuário.

Pense em transformações em quatro amplas classificações: conversão, rotação, dimensionamento e distorção. Para a finalidade de usar APIs de elementos gráficos para mudar a aparência dos elementos da interface do usuário, normalmente é mais fácil criar transformações que só definem uma operação por vez. Assim, o Windows Runtime define uma classe discreta para cada uma destas classificações de transformação:

Destas, você provavelmente usará TranslateTransform e ScaleTransform com mais frequência para cenários da interface do usuário.

Você pode combinar as transformações, e há duas classes de Windows Runtime que dão suporte a isso: CompositeTransform e TransformGroup. Em uma CompositeTransform, as transformações são aplicadas nessa ordem: dimensionamento, distorção, rotação e conversão. Use TransformGroup em vez de CompositeTransform para que as transformações sejam aplicadas em uma ordem diferente. Para saber mais, veja CompositeTransform.

Transformações e layout

No layout XAML, as transformações são aplicadas depois que o cálculo de layout é concluído, então os cálculos de espaço disponível e outras decisões de layout foram tomadas antes da aplicação das transformações. Como o layout vem primeiro, você às vezes obterá resultados inesperados se transformar elementos que estão em uma célula Grid ou um contêiner de layout semelhante que aloca espaço durante o layout. O elemento transformado poderá parecer truncado ou obscurecido porque está tentando desenhar em uma área que não calculou as dimensões pós-transformação ao dividir espaço em seu contêiner pai. Talvez seja necessário experimentar os resultados da transformação e ajustar algumas configurações. Por exemplo, em vez de usar o layout adaptável e o dimensionamento em estrela, você pode precisar alterar as propriedades Center ou declarar medidas de pixel fixo para o espaço do layout, a fim de verificar se o pai aloca espaço suficiente.

Observação de migração: Windows Presentation Foundation (WPF) tinha uma propriedade LayoutTransform que aplicava as transformação antes da passagem de layout. Porém, o XAML do Windows Runtime não aceita uma propriedade LayoutTransform. (O Microsoft Silverlight não tinha essa propriedade também.)

Como alternativa, o Kit de Ferramentas da Comunidade do Windows oferece o LayoutTransformControl, que aplica transformações de Matriz a qualquer FrameworkElement do aplicativo.

Aplicar uma transformação em um elemento de interface do usuário

Ao aplicar uma transformação a um objeto, normalmente você a usa para definir a propriedade UIElement.RenderTransform. A definição dessa propriedade não muda literalmente o objeto pixel por pixel. O que essa propriedade realmente faz é aplicar a transformação dentro do espaço de coordenadas local em que o objeto existe. Assim, a lógica e a operação de renderização (pós-layout) renderiza os espaços de coordenadas combinados, dando a impressão de que o objeto mudou a aparência e também possivelmente a posição do layout (se a TranslateTransform foi aplicada).

Por padrão, cada transformação de renderização é centralizada na origem do sistema de coordenadas local do objeto de destino, ou seja, (0,0). A única exceção é uma TranslateTransform, que não tem propriedades centrais a ser definidas porque o efeito de conversão é o mesmo independentemente de onde esteja centralizado. Mas cada uma das outras transformações tem propriedades que definem os valores CenterX e CenterY.

Sempre que você usar transformações com UIElement.RenderTransform, lembre-se de que há outra propriedade em UIElement que afeta a maneira como a transformação se comporta: RenderTransformOrigin. O que RenderTransformOrigin declara é se a transformação inteira deve ser aplicada ao ponto padrão (0,0) de um elemento ou a algum outro ponto de origem dentro do espaço de coordenadas relativas desse elemento. Para elementos típicos, (0,0) coloca a transformação no canto superior esquerdo. Dependendo do efeito que você está tentando, você pode escolher mudar RenderTransformOrigin, em vez de ajustar os valores CenterX e CenterY nas transformações. Observe que, se você aplicar os valores RenderTransformOrigin e CenterXCenterY, os resultados poderão ser bastante confusos, especialmente se você estiver animando qualquer um dos valores.

Para fins de teste por pressionamento, um objeto para o qual uma transformação é aplicada continua respondendo para inserir de uma maneira que seja consistente com sua aparência visual no espaço x-y. Por exemplo, se você usou TranslateTransform para mover um Rectangle 400 pixels lateralmente na interface do usuário, esse Rectangle poderá responder a eventos PointerPressed quando o usuário pressionar o ponto em que o Rectangle aparece visualmente. Você não obterá falsos eventos se o usuário pressionar a área em que o Rectangle estava antes de ser convertido. Para quaisquer considerações de índice z que afetam o teste de acertos, a aplicação de uma transformação não faz diferença; o índice z que rege qual elemento manipula eventos de entrada para um ponto no espaço x-y ainda é avaliado usando a ordem de filho conforme declarado em um contêiner. Essa ordem normalmente é a mesma da ordem em que você declararia os elementos em XAML, apesar de que, para os elementos filho de um objeto Canvas, é possível ajustá-la aplicando a propriedade anexada Canvas.ZIndex aos elementos filho.

Outras propriedades de uma transformação

  • Brush.Transform, Brush.RelativeTransform: Elas influenciam a maneira como Brush usa o espaço de coordenadas na área em que Brush é aplicado para definir propriedades visuais como primeiro plano e tela de fundo. Essas transformações não são relevantes para os pincéis mais comuns (que geralmente estão definindo cores sólidas com SolidColorBrush), mas podem ser ocasionalmente úteis ao pintar áreas com ImageBrush ou LinearGradientBrush.
  • Geometry.Transform: Você pode usar essa propriedade para aplicar uma transformação a uma geometria antes de usar essa geometria para um valor de propriedade Path.Data.

Animar uma transformação

Os objetos Transform podem ser animados. Para animar uma Transform, aplique uma animação de um tipo compatível à propriedade que você quer animar. Isso normalmente significa que você está usando os objetos DoubleAnimation ou DoubleAnimationUsingKeyFrames para definir a animação, pois todas as propriedades das transformações são do tipo Double. Animações que afetam uma transformação que é usada para um valor UIElement.RenderTransform não são consideradas animações dependentes, mesmo se têm uma duração diferente de zero. Para saber mais sobre animações dependentes, consulte Animações com storyboard.

Se você animar as propriedades para produzir um efeito semelhante a uma transformação em termos da aparência visual líquida, por exemplo, animando a Width e Height de um FrameworkElement em vez de aplicar um TranslateTransform—essas animações são quase sempre tratadas como animações dependentes. Você teria de habilitar as animações e poderia haver problemas de desempenho significativos com a animação, principalmente se você estiver tentando dar suporte à interação do usuário enquanto esse objeto está sendo animado. Por isso, é preferível usar uma transformação e animá-la em vez de animar qualquer outra propriedade em que a animação seria tratada como uma animação dependente.

Para direcionar a transformação, é necessário haver uma Transform existente como valor de RenderTransform. Normalmente, você coloca um elemento para o tipo de transformação apropriado no XAML inicial, às vezes sem propriedades definidas nessa transformação.

Geralmente você usa uma técnica de direcionamento indireto para aplicar animações às propriedades de uma transformação. Para saber mais sobre a sintaxe de direcionamento indireto, consulte Animações com storyboard e Sintaxe de caminhos de propriedades.

Os estilos padrão para controles às vezes definem as animações das transformações como parte de seu comportamento de estado visual. Por exemplo, os estados visuais de ProgressRing usam valores RotateTransform animados para "girar" os pontos do anel.

A seguir está um exemplo simples de como animar uma transformação. Nesse caso, ocorre a animação do Angle de uma RotateTransform para girar um Rectangle no lugar em torno de seu centro visual. Este exemplo nomeia RotateTransform para que não precise de direcionamento indireto de animação, mas você pode deixar a transformação sem nome, nomear o elemento ao qual a transformação é aplicada e usar direcionamento indireto, como .

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

Justificar quadros de referência de coordenadas em runtime

UIElement tem um método chamado TransformToVisual, que pode gerar um Transform que correlaciona os quadros de referência de coordenadas de dois elementos de interface do usuário. Você pode usar esse método para comparar um elemento com o quadro de referência de coordenadas padrão do aplicativo se calcular o visual da raiz como primeiro parâmetro. Isso pode ser útil se você capturou um evento de entrada de um elemento diferente ou se está tentando prever o comportamento do layout sem na verdade solicitar um cálculo de layout.

Dados de evento obtidos de eventos de ponteiro oferecem acesso a um método GetCurrentPoint, em que é possível especificar um parâmetro relativeTo para mudar o quadro de referência de coordenadas para um elemento específico em vez do aplicativo padrão. Isso simplesmente aplica uma transformação de conversão internamente e transforma os dados de coordenadas x-y para você ao criar o objeto PointerPoint retornado.

Descrever uma transformação matematicamente

Uma transformação pode ser descrita em termos de uma matriz de transformação. Uma matriz 3x3 é usada para descrever as transformações em um plano x-y bidimensional. Matrizes de transformação afins podem ser multiplicadas para formar qualquer número de transformações lineares, como rotação e distorção, seguidas de conversão. A coluna final de uma matriz de transformação afim é igual a (0, 0, 1), portanto você precisa especificar apenas os membros das duas primeiras colunas na descrição matemática.

A descrição matemática de uma transformação pode ser útil se você tiver conhecimentos matemáticos ou familiaridade com técnicas de programação de elementos gráficos que também usem matrizes para descrever transformações do espaço de coordenadas. Há uma classe derivada de Transform que permite expressar uma transformação diretamente em termos de sua matriz 3x3: MatrixTransform. MatrixTransform tem uma propriedade Matrix, que mantém uma estrutura de seis propriedades: M11, M12, M21, M22, OffsetX e OffsetY. Cada propriedade Matrix usa um valor Double e corresponde a seis valores relevantes (colunas 1 e 2) de uma matriz de transformação afim.

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

Qualquer transformação que você poderia descrever com um objeto TranslateTransform, ScaleTransform, RotateTransform ou SkewTransform poderia ser perfeitamente descrita por uma MatrixTransform com um valor Matrix. Mas normalmente você usa TranslateTransform e as outras, porque as propriedades nessas classes de transformação são mais fáceis de conceitualizar do que definir os componentes vetoriais em uma Matrix. Também é mais fácil animar as propriedades discretas dessas transformações; enquanto uma Matrix na verdade é uma estrutura e não um DependencyObject, por isso não pode dar suporte a valores individuais animados.

Algumas ferramentas de design XAML que permitem aplicar operações de transformação serializarão os resultados como uma MatrixTransform. Nesse caso, convém usar a mesma ferramenta de design outra vez para mudar o efeito de transformação e serializar novamente o XAML, em vez de tentar manipular os valores Matrix diretamente sozinho no XAML.

Transformações 3D

No Windows 10, o XAML introduziu uma nova propriedade, UIElement.Transform3D, que pode ser usada para criar efeitos 3D com interface de usuário. Para fazer isso, use PerspectiveTransform3D para adicionar uma perspectiva 3D compartilhada ou "câmera" à sua cena e, em seguida, use CompositeTransform3D para transformar um elemento no espaço 3D, como você usaria CompositeTransform. Veja UIElement.Transform3D para transformações de uma discussão sobre como implementar 3D.

Para efeitos 3D mais simples que se aplicam somente a um único objeto, a propriedade UIElement.Projection pode ser usada. Usar um PlaneProjection como o valor dessa propriedade é equivalente a aplicar uma transformação de perspectiva fixa e uma ou mais transformações 3D ao elemento. Esse tipo de transformação é descrito em mais detalhe em Efeitos de perspectiva 3D para interface do usuário de XAML.