No post anterior vimos como animar uma transição usando código. Como falei, não acho aquela a melhor solução, pois obriga a usar code behind, o que não é de fácil manutenção. Poderíámos refatorar o código, criando uma classe para a animação e usá-la. Isto traria um pouco mais de separação, mas ainda teríamos de usar code behind.
Nesta segunda parte, usaremos um enfoque diferente: o uso de componentes prontos. Podemos usar diversos componentes, como o Kevin Bag-O-Tricks (https://github.com/thinkpixellab/bot), FluidKit (http://fluidkit.com), Silverlight Toolkit (http://silverlight.codeplex.com – só para Silverlight), o Transitionals (http://transitionals.codeplex.com).
Usaremos aqui o Transitionals, para WPF. Se quisermos fazer animações em Silverlight, devemos escolher outro dos componentes acima.
Seu uso é muito simples: após baixar o componente e adicionar uma referência ao projeto para a dll Transitionals.dll, devemos adicionar um componente TransitionElement no local onde queremos a animação, configurar a animação e colocar um conteúdo para o componente. Ao mudar o conteúdo, ocorre a transição selecionada.
Vamos então fazer o nosso projeto de animação. Crie um novo projeto WPF e adicione uma referência a Transitionals.dll. Em seguida, coloque um TransitionElement na grid principal:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<transc:TransitionElement x:Name="TransitionBox">
<transc:TransitionElement.Transition>
<transt:TranslateTransition StartPoint="1,0" EndPoint="0,0" Duration="0:0:1"/>
</transc:TransitionElement.Transition>
</transc:TransitionElement>
<Button Width="65" Grid.Row="1" Content="Esconde" Margin="5" Click="Button_Click" />
</Grid>
Devemos definir os namespaces para o TransitionElement e para a TranslateTransition na definição da janela:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:transc="clr-namespace:Transitionals.Controls;assembly=Transitionals"
xmlns:transt="clr-namespace:Transitionals.Transitions;assembly=Transitionals"
Title="MainWindow" Height="350" Width="525">
Em seguida, é só colocar um conteúdo no TransitionElement:
<transc:TransitionElement x:Name="TransitionBox">
<transc:TransitionElement.Transition>
<transt:TranslateTransition StartPoint="1,0" EndPoint="0,0" Duration="0:0:1"/>
</transc:TransitionElement.Transition>
<Grid Background="Red" />
</transc:TransitionElement>
O código do botão muda o conteúdo do TrasitionElement e, com isso ativa a transição:
private void Button_Click(object sender, RoutedEventArgs e)
{
TransitionBox.Content = new Grid() {Background = Brushes.Blue};
}
Desta maneira, o código fica muito mais fácil, só precisamos mudar o conteúdo do elemento. Além disso, o componente Transitionals tem muitos tipos de transições, e podemos configurá-las de diversas maneiras. Por exemplo, o TranslateTrasition tem as propriedades StartPoint e EndPoint, dizendo onde começa e onde termina a transição. Para fazer da esquerda para direita, StartPoint deve ser –1,0 e EndPoint, 0,0. De cima para baixo, StartPoint deve ser 0,-1 e EndPoint, 0,0. Podemos inclusive fazer uma transição diagonal usando os pontos 1,1 e 0,0.
Eliminando o Code Behind
Uma das coisas que podem ser melhoradas aqui é a eliminação do code behind, usando o padrão de projeto MVVM. Para isso, usaremos o framework MVVM Light, que pode ser obtido gratuitamente em , ou ainda instalado diretamente no projeto usando o Nuget, uma extensão para o Visual Studio que facilita o download e instalação de bibliotecas e ferramentas no Visual Studio. Se você ainda não baixou o Nuget, vá imediatamente para e baixe-o. Vale a pena!
Uma vez instalado o Nuget, clique com o botão direito em References, no Solution Explorer e selecione “Manage Nuget Packages”. Tecle “mvvm” na caixa de pesquisa e instale o pacote MVVM Light:
Isto instala o MVVM Light em nosso projeto, adiciona as referências necessárias e cria uma pasta ViewModel, com dois arquivos, MainViewModel.cs e ViewModelLocator.cs. MainViewModel.cs é o ViewModel referente à janela princiapl e ViewModelLocator é um localizador de ViewModel, referente à View.
Em MainViewModel.cs, colocamos uma propriedade, do tipo ViewModelBase, que conterá os ViewModel referente à View do conteúdo atual:
private ViewModelBase conteudo;
public ViewModelBase Conteudo
{
get { return conteudo; }
set
{
conteudo = value;
RaisePropertyChanged("Conteudo");
}
}
Criaremos em seguida dois ViewModels, que serão referentes à nossas Views. Os dois ViewModels são muito semelhantes e têm apenas uma propriedade:
public class ViewModelA : ViewModelBase
{
private string texto;
public string Texto
{
get { return texto; }
set
{
texto = value;
RaisePropertyChanged("Texto");
}
}
}
public class ViewModelB : ViewModelBase
{
private string texto;
public string Texto
{
get { return texto; }
set
{
texto = value;
RaisePropertyChanged("Texto");
}
}
}
Em seguida, crie no Solution Explorer um diretório chamado View e coloque lá dois UserControls, com apenas uma Grid com um TextBlock:
<UserControl x:Class="WpfApplication2.View.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="Red">
<TextBlock Text="{Binding Texto}" FontSize="36" />
</Grid>
</UserControl>
Para eliminar o code behind, precisamos fazer um data binding da propriedade Content do TransitionElement com a propriedade conteúdo do ViewModel:
<transc:TransitionElement x:Name="TransitionBox" Content="{Binding Conteudo}">
<transc:TransitionElement.Transition>
<transt:TranslateTransition StartPoint="1,0" EndPoint="0,0" Duration="0:0:1"/>
</transc:TransitionElement.Transition>
</transc:TransitionElement>
e eliminar o clique do botão, substituindo-o por um Command:
<Button Width="65" Grid.Row="1" Content="Esconde" Margin="5" Command="{Binding EscondeCommand}" />
O command é definido no MainViewModel:
private ICommand escondeCommand;
public ICommand EscondeCommand
{
get { return escondeCommand ?? (escondeCommand = new RelayCommand(MudaConteudo)); }
}
private void MudaConteudo()
{
Conteudo = conteudo is ViewModelA ?
(ViewModelBase)new ViewModelB() { Texto = "ViewModel B"} :
(ViewModelBase)new ViewModelA() {Texto = " ViewModel A"};
}
Finalmente, devemos definir o DataContext para a View principal:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:transc="clr-namespace:Transitionals.Controls;assembly=Transitionals"
xmlns:transt="clr-namespace:Transitionals.Transitions;assembly=Transitionals"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding Source={StaticResource Locator}, Path=Main}">
Ao executar o programa, temos algo como mostrado na figura abaixo:
A view não é mostrada, apenas o nome da classe do conteúdo. Isso era de se esperar, pois a propriedade Conteúdo é do tipo ViewModelBase e sua representação é o método ToString(). Poderíamos ter colocado a View como sendo o conteúdo, mas isto vai contra o padrão MVVM: o ViewModel não deve conhecer a View. Uma solução para mostrar a View a partir do ViewModel é usar um recurso disponível no WPF ou no Silverlight 5: Data Templates implícitos. Em um Data Template implicito, não explicitamos a Key, apenas dizemos qual é o DataType e, com isso o WPF/Silverlight renderezam este DataTemplate toda vez que um conteúdo for do tipo referente a ele.
Definimos então os DataTemplates na seção Resources da janela:
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:ViewModelA}" >
<View:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:ViewModelB}" >
<View:ViewB />
</DataTemplate>
</Window.Resources>
Agora, ao executar, temos a transição entre as duas views, sem o uso de code behind.
Conclusões
Usando componentes de animação de transições, temos a possibilidade de fazer transições muito sofisticadas, de maneira muito simples bastando mudar o conteúdo do componente.
Em seguida, vimos como tirar o código do code behind, colocando-o em um ViewModel, o que facilita na manutenção e permite maior testabilidade do código. Como bonus, vimos como usar implicit templates para ligar uma view a um viewmodel sem usar código. Este recurso está disponível apenas no WPF e no Silverlight 5. Embora você possa achar que não vale a pena (eliminamos apenas uma linha de código e incluímos dois viemodels, duas views, um novo componente MVVM, entre outros), minha intenção aqui foi mostrar como usar outros recursos, como a introdução do MVVM ao invés do code behind e como usar novos recursos, como o DataTemplate implícito. Numa aplicação maior, que requer maiores cuidados, estas mudanças se justificam plenamente.
Mas estas não são as únicas maneiras de se fazer transições de controles. Nos próximos artigos, veremos quais são as outras maneiras. Até lá!