Visão geral das transformações

Saiba como usar transformações na API do Tempo de Execução do Windows, alterando os sistemas de coordenadas relativas dos elementos na interface do usuário. Isso pode ser usado 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 para outro espaço de coordenadas. Quando uma transformação é aplicada a um elemento da interface do usuário, ela altera como esse elemento da interface do usuário é renderizado na tela como parte da interface do usuário.

Pense em transformações em quatro classificações amplas: translação, rotação, escala e inclinação (ou cisalhamento). Para fins de uso de APIs gráficas para alterar a aparência dos elementos da interface do usuário, geralmente é mais fácil criar transformações que definem apenas uma operação por vez. Portanto, o Tempo de Execução do Windows define uma classe discreta para cada uma dessas classificações de transformação:

Destes, é provável que você use TranslateTransform e ScaleTransform com mais frequência para cenários de interface do usuário.

Você pode combinar transformações, e há duas classes do Tempo de Execução do Windows que oferecem suporte a isso: CompositeTransform e TransformGroup. Em um CompositeTransform, as transformações são aplicadas nesta ordem: escalar, inclinar, girar, traduzir. Use TransformGroup em vez de CompositeTransform se quiser que as transformações sejam aplicadas em uma ordem diferente. Para obter mais informações, consulte CompositeTransform.

Transformações e layout

No layout XAML, as transformações são aplicadas após a conclusão da passagem de layout, portanto, os cálculos de espaço disponível e outras decisões de layout foram tomadas antes que as transformações sejam aplicadas. Como o layout vem primeiro, às vezes você obterá resultados inesperados se transformar elementos que estão em uma célula de grade ou contêiner de layout semelhante que aloca espaço durante o layout. O elemento transformado pode parecer truncado ou obscurecido porque está tentando desenhar em uma área que não calculou as dimensões pós-transformação ao dividir o espaço dentro de seu contêiner pai. Talvez seja necessário experimentar os resultados da transformação e ajustar algumas configurações. Por exemplo, em vez de confiar no layout adaptável e no dimensionamento de estrelas, talvez seja necessário alterar as propriedades do Centro ou declarar medidas de pixel fixas para espaço de layout para garantir que o pai aloque espaço suficiente.

Observação de migração: o Windows Presentation Foundation (WPF) tinha uma propriedade LayoutTransform que aplicava transformações antes do passo de layout. Mas o XAML do Tempo de Execução do Windows não oferece suporte a uma propriedade LayoutTransform . (O Microsoft Silverlight também não tinha essa propriedade.)

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

Quando você aplica uma transformação a um objeto, normalmente faz isso para definir a propriedade UIElement.RenderTransform. A definição dessa propriedade não altera literalmente o objeto pixel por pixel. O que a propriedade realmente faz é aplicar a transformação dentro do espaço de coordenadas local no qual esse objeto existe. Em seguida, a lógica de renderização e a operação (pós-layout) renderizam os espaços de coordenadas combinados, fazendo parecer que o objeto mudou de aparência e também potencialmente sua posição de layout (se TranslateTransform foi aplicado).

Por padrão, cada transformação de renderização é centralizada na origem do sistema de coordenadas locais do objeto de destino — seu (0,0). A única exceção é um TranslateTransform, que não tem propriedades de centro para definir porque o efeito de tradução é o mesmo, independentemente de onde ele está centrado. Mas as outras transformações têm propriedades que definem valores CenterX e CenterY .

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

Para fins de teste de acertos, um objeto ao qual uma transformação é aplicada continua a responder à entrada de uma maneira esperada que é consistente com sua aparência visual no espaço x-y. Por exemplo, se você tiver usado um TranslateTransform para mover um Rectangle 400 pixels lateralmente na interface do usuário, esse Rectangle responderá a eventos PointerPressed quando o usuário pressionar o ponto onde o Rectangle aparece visualmente. Você não obterá eventos falsos se o usuário pressionar a área onde o Retângulo estava antes de ser traduzido. Para quaisquer considerações de índice z que afetem o teste de acertos, a aplicação de uma transformação não faz diferença; O índice z que controla qual elemento manipula eventos de entrada para um ponto no espaço X-Y ainda é avaliado usando a ordem filho conforme declarado em um contêiner. Essa ordem geralmente é a mesma em que você declara os elementos em XAML, embora para elementos filho de um objeto Canvas você possa ajustar a ordem aplicando a propriedade anexada Canvas.ZIndex a elementos filho.

Outras propriedades de uma transformação

Animar uma transformação

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

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

Para direcionar a transformação, deve haver um Transform existente como o 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.

Normalmente, 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, veja Animações de storyboard e Sintaxe de caminho de propriedade.

Estilos padrão para controles às vezes definem animações de 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 no anel.

Aqui está um exemplo simples de como animar uma transformação. Nesse caso, ele está animando o Ângulo de um RotateTransform para girar um Retângulo no lugar em torno de seu centro visual. Este exemplo nomeia o RotateTransform para que não precise de direcionamento de animação indireta, mas você pode alternativamente deixar a transformação sem nome, nomear o elemento ao qual a transformação é aplicada e usar a segmentação indireta, 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();
}

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 isso para comparar um elemento com o quadro de referência de coordenadas padrão do aplicativo se passar o visual raiz como o primeiro parâmetro. Isso pode ser útil se você capturou um evento de entrada de um elemento diferente ou se estiver tentando prever o comportamento do layout sem realmente solicitar uma passagem de layout.

Os dados de evento obtidos de eventos de ponteiro fornecem acesso a um método GetCurrentPoint, onde você pode especificar um parâmetro relativeTo para alterar o quadro de referência de coordenadas para um elemento específico em vez do padrão do aplicativo. Essa abordagem simplesmente aplica uma transformação de tradução internamente e transforma os dados de coordenadas x-y para você quando cria 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 3×3 é usada para descrever as transformações em um plano bidimensional, x-y. As matrizes de transformação afim podem ser multiplicadas para formar qualquer número de transformações lineares, como rotação e inclinação (cisalhamento), seguidas de translaçã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 para você se você tiver uma formação matemática ou uma familiaridade com técnicas de programação gráfica que também usam 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 3×3: MatrixTransform. MatrixTransform tem uma propriedade Matrix, que contém uma estrutura que tem seis propriedades: M11, M12, M21, M22, OffsetX e OffsetY. Cada propriedade Matrix usa um valor Double e corresponde aos 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ê possa descrever com um objeto TranslateTransform, ScaleTransform, RotateTransform ou SkewTransform pode ser descrita igualmente por um MatrixTransform com um valor Matrix. Mas você normalmente usa apenas TranslateTransform e os outros porque as propriedades nessas classes de transformação são mais fáceis de conceituar do que definir os componentes vetoriais em uma Matrix. Também é mais fácil animar as propriedades discretas das transformadas; uma Matrix é na verdade uma estrutura e não um DependencyObject, portanto, não pode oferecer suporte a valores individuais animados.

Algumas ferramentas de design XAML que permitem aplicar operações de transformação serializarão os resultados como um MatrixTransform. Nesse caso, talvez seja melhor usar a mesma ferramenta de design novamente para alterar o efeito de transformação e serializar o XAML novamente, em vez de tentar manipular os valores da Matrix diretamente 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 a interface do 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. Consulte UIElement.Transform3D para obter uma discussão sobre como implementar transformações 3D.

Para efeitos 3D mais simples que se aplicam apenas a um único objeto, a propriedade UIElement.Projection pode ser usada. Usar um PlaneProjection como o valor para essa 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 com mais detalhes em efeitos de perspectiva 3D para interface do usuário XAML.