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 relativos 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 a forma 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 de cada 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. Num CompositeTransform, as transformações são aplicadas nesta ordem: escala, inclinação, rotação, translação. 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 do passo de layout, portanto, 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 Grid 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 depender do layout adaptável e do dimensionamento em estrela, talvez seja necessário alterar as propriedades do Center ou declarar medidas de pixel fixas para o 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 executava transformações antes da passagem de layout. Mas o XAML do Windows Runtime não oferece suporte à propriedade LayoutTransform . (O Microsoft Silverlight também não tinha essa propriedade.)

Sugestão

Como alternativa, o Kit de Ferramentas da Comunidade do Windows fornece o LayoutTransformControl que aplica transformações Matrix em qualquer FrameworkElement da sua aplicação.

Aplicando uma transformação a um elemento da interface do usuário

Quando você aplica uma transformação a um objeto, normalmente o faz para definir a propriedade UIElement.RenderTransform. Definir essa 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 locais 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 aplicada).

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 está centralizado. Mas as outras transformações cada uma tem propriedades que definem os valores de 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 toda a transformação 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 relativo desse elemento. Para elementos típicos, (0,0) coloca a transformação no canto superior esquerdo. Dependendo do efeito desejado, podes optar por alterar RenderTransformOrigin em vez de ajustar os valores CenterX e CenterY em transformações. Observe que, se você aplicar RenderTransformOrigin e valores de CenterXCenterY, os resultados podem ser bastante confusos, especialmente se você estiver animando qualquer um dos valores.

Para fins de teste de acerto, um objeto ao qual uma transformação é aplicada continua a responder à entrada de uma maneira esperada que seja consistente com sua aparência visual no espaço x-y. Por exemplo, se tu usaste um TranslateTransform para mover um Rectangle 400 pixels lateralmente na UI, esse Rectangle responde a eventos PointerPressed quando o utilizador pressiona o ponto onde o Rectangle aparece visualmente. Você não obterá eventos falsos se o usuário pressionar a área onde o Rectangle 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 governa 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 que a ordem na qual você declara os elementos em XAML, embora, para os elementos filhos de um objeto Canvas, você possa ajustar a ordem aplicando a propriedade anexada Canvas.ZIndex aos elementos filhos.

Outras propriedades de transformação

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

Animar uma transformação

Objetos de Transform podem ser animados. Para animar uma Transform, aplique à propriedade que pretende animar uma animação de um tipo compatível. Isso normalmente significa que está a usar objetos do tipo DoubleAnimation ou DoubleAnimationUsingKeyFrames para especificar a animação, porque todas as propriedades de transformação são do tipo Double. As animações que afetam uma transformação que é utilizada num valor de UIElement.RenderTransform não são consideradas animações dependentes, mesmo que tenham uma duração não nula. Para obter mais informações sobre animações dependentes, consulte Animações com storyboard.

Se animar propriedades para produzir um efeito semelhante ao de uma transformação, em termos do aspeto visual global — por exemplo, animando a Width e a Height de um FrameworkElement em vez de aplicar um TranslateTransform — essas animações sã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 esse motivo, é 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 ter como alvo a transformação, deve haver um Transform como o valor para 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 segmentação indireta, veja animações com storyboard e Sintaxe de caminho de propriedade.

Os 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 para ProgressRing usam valores de RotateTransform animados para "fazer rodar" os pontos no anel.

Aqui está um exemplo simples de como animar uma transformação. Neste caso, está a animar o Angle de uma RotateTransform para fazer rodar um Rectangle no mesmo lugar em torno do seu centro visual. Este exemplo nomeia o RotateTransform, pelo que não precisa de direcionamento de animação indireta, mas, alternativamente, pode-se deixar a transformação sem nome, nomear o elemento ao qual a transformação é aplicada e usar um alvo indireto, 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();
}

Contabilização de quadros de referência de coordenadas em tempo real

UIElement tem um método chamado TransformToVisual, que pode gerar um Transform que correlaciona os quadros de coordenadas de referência para dois elementos da 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.

Observação

(PTU) Os dados de evento obtidos de eventos de ponteiro fornecem acesso a um método GetCurrentPoint, onde podes especificar um parâmetro relativeTo para alterar o referencial de coordenadas para um elemento específico em vez do padrão da aplicação. Essa abordagem simplesmente aplica internamente uma transformação de translação e transforma os dados de coordenadas x-y ao criar o objeto PointerPoint retornado.

Descrevendo matematicamente uma transformação

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 coordenado. 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 possui uma estrutura que tem seis propriedades: M11, M12, M21, M22, OffsetX e OffsetY. Cada propriedade Matrix utiliza 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 DeslocamentoY 1

Qualquer transformação que você possa descrever com um TranslateTransform, ScaleTransform, RotateTransformou um SkewTransform pode ser igualmente descrita por um MatrixTransform com um valor de Matrix . Mas normalmente você 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 transformações; uma Matrix é, na verdade, uma estrutura e não um DependencyObject, portanto, não pode suportar valores individuais animados.

Algumas ferramentas de design XAML que permitem aplicar operações de transformação irão serializar os resultados como um MatrixTransform. Neste caso, talvez seja melhor voltar a usar a mesma ferramenta de design para alterar o efeito de transformação e voltar a serializar o XAML, em vez de tentar manipular diretamente, por si próprio, os valores da Matrix 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 perspetiva 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 dessa propriedade é equivalente a aplicar uma transformação de perspetiva fixa e uma ou mais transformações 3D para o elemento. Esse tipo de transformação é descrito com mais detalhes em efeitos de perspetiva 3D para a interface do usuário XAML.