WPF Styles: Estilizando sua aplicação

Veja neste artigo como utilizar Styles em WPF para facilitar a formatação visual da sua aplicação e evitar repetição de código XAML.

O WPF, bem como os demais tipos de projetos baseados em XAML (Windows Phone e Windows Store Apps para Windows 8), possui entre seus vários diferenciais (se comparado com projetos Windows Forms) a possibilidade de criar interfaces mais bem elaboradas, com várias customizações que, dependendo do tipo aplicação, podem ser fundamentais para que o cliente opte por utilizá-la.

Diferente das aplicações Windows Forms, o layout do WPF é baseado em uma linguagem de marcação, o que inclusive facilita o aprendizado desse framework para desenvolvedores com experiência prévia em HTML. E semelhante ao que ocorre no desenvolvimento web com HTML e CSS, com XAML também é possível formatar diversas características visuais dos componentes, tanto individualmente, quanto de forma global utilizando Styles. Essa grande flexibilidade permite ao desenvolvedor padronizar o design de sua aplicação sem precisar reescrever toda a formatação para cada componente, e ao mesmo tempo o deixa livre para customizar um único controle quando for necessário.

Para que o uso de styles faça mais sentido, vamos iniciar imaginando um cenário real em que desejemos padronizar a aparência de todas as telas de nossa aplicação utilizando determinadas cores, fontes, tamanhos e etc. Normalmente, faríamos a configuração de cada controle individualmente, definindo propriedades como Background, Foreground, FontFamily, FontSize, entre outras. Porém, se considerarmos que a maioria dos controles de um determinado tipo (botões, labels, campos de texto, etc.) terão a mesma formatação, essa logo se mostra uma forma bastante ineficiente, pois claramente haverá muita repetição de código e alterações nessas formatações seriam bastante custosas, uma vez que seria necessário alterar as propriedades de todos os controles, em todas as telas.

Na Listagem 1 temos um exemplo de uma tela relativamente pequena em que são feitas várias formatações visuais individualmente em cada componente. Neste caso são algumas poucas linhas de código, mas a partir delas podemos ter uma ideia do que precisaríamos fazer em um projeto real de grande porte.

Listagem 1. Formatações individuais

<Window x:Class="WpfApplication7.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Cadastro do cliente" Height="200" Width="525" FontFamily="Century Gothic" FontSize="16" WindowStartupLocation="CenterScreen"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Margin="10"> <DockPanel> <TextBlock Text="Nome" Width="100"/> <TextBox /> </DockPanel> <DockPanel> <TextBlock Text="E-mail" Width="100"/> <TextBox/> </DockPanel> <DockPanel> <TextBlock Text="Endereço" Width="100"/> <TextBox /> </DockPanel> <DockPanel> <TextBlock Text="Telefone" Width="100"/> <TextBox /> </DockPanel> </StackPanel> <StackPanel Grid.Row="1" Orientation="Horizontal"> <Button Content="Salvar" Width="100" Height="30"/> <Button Content="Cancelar" Width="100" Height="30"/> <Button Content="Excluir" Width="100" Height="30"/> </StackPanel> </Grid> </Window>

O resultado desse código pode ser visto na tela presente na Figura 1.

Figura 1. Tela com controles formatados individualmente

Vejamos agora como reduzir essa repetição de código utilizando styles, que funcionam de forma semelhante ao CSS (Cascading Style Sheets) para HTML.

A sintaxe básica para adição de um estilo em XAML é apresentada no código da Listagem 2.

Listagem 2. Sintaxe para adição de estilos

<Style x:Key="nome" TargetType="tipo"> <Setter Property="propriedade1" Value="valor1 "/> <Setter Property="propriedade2" Value="valor2"/> <Setter Property="propriedade3" Value="valor3"/> </Style>

O atributo Key define o nome pelo qual aquele estilo pode ser referenciado no restante do código. Em TargetType definimos o tipo de componente que o estilo irá formatar, como Button ou TextBlock. Caso se omita a propriedade Key, o estilo irá atuar automaticamente sobre todos os componentes daquele tipo que tenham acesso a ele. Já se a propriedade Key for definida, o estilo só funcionará em controles que o referenciem diretamente em sua propriedade Style.

Nos elementos Setter precisamos definir os atributos Property, que indicam qual propriedade do componente nós queremos formatar, e Value, que representa o valor que queremos atribuir àquela propriedade. O Intellisense do Visual Studio funciona tanto nessas duas propriedades quanto na TargetType, auxiliando durante a criação dos estilos.

A tag Style deve ser adicionada dentro da propriedade Resource do container que contiver os elementos que desejamos formatar. Por exemplo, se no exemplo anterior criássemos um style dentro do segundo StackPanel, ele só funcionaria sobre os componentes Button ou qualquer outro que colocássemos ali dentro. Se adicionássemos o estilo dentro dos Resources do objeto Window, ele poderia atuar sobre todos os componentes da tela.

Para facilitar o entendimento, vamos voltar ao código da Listagem 1 e fazer algumas alterações. Vamos começar removendo os atributos FontSize e FontFamily da tag Window e os atributos Width e Height dos Buttons e TextBlocks, de acordo com a Listagem 3.

Listagem 3. Remoção das formatações individuais

<Window x:Class="WpfApplication7.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Cadastro do cliente" Height="200" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Margin="10"> <DockPanel> <TextBlock Text="Nome"/> <TextBox /> </DockPanel> <DockPanel> <TextBlock Text="E-mail"/> <TextBox/> </DockPanel> <DockPanel> <TextBlock Text="Endereço"/> <TextBox /> </DockPanel> <DockPanel> <TextBlock Text="Telefone"/> <TextBox /> </DockPanel> </StackPanel> <StackPanel Grid.Row="1" Orientation="Horizontal"> <Button Content="Salvar"/> <Button Content="Cancelar"/> <Button Content="Excluir"/> </StackPanel> </Grid> </Window>

Automaticamente vemos que toda a tela perde sua formatação, como mostra a Figura 2, mas agora começaremos a utilizar os estilos locais para formatar cada tipo de componente.

Figura 2. Tela sem formatações

Dentro do primeiro StackPanel, adicione o elemento StackPanel.Resources e dentro dele um estilo de acordo com a Listagem 4.

Listagem 4. Estilo local para os TextBlocks no StackPanel

... <StackPanel Grid.Row="0" Margin="10"> <StackPanel.Resources> <Style TargetType="TextBlock"> <Setter Property="Width" Value="100"/> </Style> </StackPanel.Resources> ...

Neste caso estamos definindo um estilo que formatará todos os controles do tipo TextBlock que estejam dentro desse StackPanel. Observe que automaticamente todos os TextBlocks assumem a largura igual a 100, como mostra a Figura 3, e só precisamos definir isso uma vez.

Figura 3. TextBlocks do StackPanel formatados

Agora no segundo StackPanel, o que contém os botões do rodapé, adicione estilo seguindo o código da Listagem 5. Dessa vez definimos a largura e altura dos botões daquele StackPanel para 100 e 30, respectivamente. O resultado, visto automaticamente em tempo de design, é mostrado na Figura 4.

Listagem 5. Estilo local para os Buttons do StackPanel

<StackPanel.Resources> <Style TargetType="Button"> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="30"/> </Style> </StackPanel.Resources>

Figura 4. Botões do StackPanel Formatados

Para voltar à formatação inicial, agora resta apenas definir a fonte dos controles. Neste caso, essa formatação poderia ser feita de várias formas. Por exemplo, poderíamos definir um Setter para as propriedades FontFamily e FontSize dentro de cada um dos estilos já criados, porém, como todos os controles da tela devem utilizar a mesma fonte, precisaremos aqui de um estilo mais externo. Outra forma seria criar dois novos estilos dentro do Grid que contém todos os controles e formatar tanto os TextBlocks quanto os Buttons, mas essa também não parece ser uma solução elegante ou viável. A melhor alternativa aqui é criar um estilo dentro da tag Window formatando ela mesma, assim, todos os controles que estiverem dentro da janela receberão essa formatação. Esse estilo é apresentado na Listagem 6.

Listagem 6. Estilo no objeto Window

<Window.Resources> <Style TargetType="Window"> <Setter Property="FontFamily" Value="Century Gothic"/> <Setter Property="FontSize" Value="16"/> </Style> </Window.Resources>

O resultado agora é o desejado, mas esse estilo especificamente não precisaria ser criado aí, pois bastaria definir esses atributos na própria tag Window, como já estava feito.

Para que essa formatação, que é geral para todas as janelas da aplicação possa ser realmente reaproveitada da melhor forma possível, devemos criar esse estilo no código XAML da própria aplicação, ou seja, dentro do arquivo App.xaml, entre as tags Application.Resources. Podemos inclusive mover todos os estilos já criados para lá e removê-los dos locais onde foram criados anteriormente. A Listagem 7 mostra como deve ficar o arquivo App.xaml após a adição dos estilos.

Listagem 7. Adição de estilos globais para toda a aplicação

<Application x:Class="WpfApplication7.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> <Application.Resources> <Style TargetType="Window"> <Setter Property="FontFamily" Value="Century Gothic"/> <Setter Property="FontSize" Value="16"/> </Style> <Style TargetType="TextBlock"> <Setter Property="Width" Value="100"/> </Style> <Style TargetType="Button"> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="30"/> </Style> </Application.Resources> </Application>

Agora todos os controles Window, TextBlock e Button da aplicação serão formatados automaticamente por esse estilo. Porém, haverá situações em que esse comportamento pode não ser desejado. Podemos precisar de um estilo global, mas que só seja utilizado em alguns controles especificamente. Para estes casos podemos criar estilos com a propriedade Key definida e utilizá-lo apenas onde for necessário.

Voltando ao arquivo App.xaml, definida a propriedade Key de cada um dos estilos, por exemplo, como mostra a Listagem 8.

Listagem 8. Definindo o nome dos estilos

... <Style TargetType="Window" x:Key="estiloJanelaCadastro"> <Setter Property="FontFamily" Value="Century Gothic"/> <Setter Property="FontSize" Value="16"/> </Style> <Style TargetType="TextBlock" x:Key="estiloTextBlockCadastro"> <Setter Property="Width" Value="100"/> </Style> <Style TargetType="Button" x:Key="estiloBotaoCadastro"> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="30"/> </Style> ...

Se voltarmos ao design da janela em que estávamos trabalhando, veremos que toda a formatação foi removida. Para aplicar os estilos apenas onde desejamos, devemos definir a propriedade Style do componente apontando para o estilo criado, seguindo a seguinte sintaxe: Style={StaticResource KeyDoEstilo}.

Na Listagem 9 temos a mesma janela já utilizada anteriormente, mas agora com apenas alguns controles utilizando estilos específicos, enquanto os outros foram deixados sem estilo para que possam ser comparados.

Listagem 9. Utilizando estilos pelo nome

<Window x:Class="WpfApplication7.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Cadastro do cliente" Height="200" Width="525" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Margin="10"> <DockPanel> <TextBlock Text="Nome" /> <TextBox /> </DockPanel> <DockPanel> <TextBlock Text="E-mail" /> <TextBox/> </DockPanel> <DockPanel> <TextBlock Text="Endereço"/> <TextBox /> </DockPanel> <DockPanel> <TextBlock Text="Telefone"/> <TextBox /> </DockPanel> </StackPanel> <StackPanel Grid.Row="1" Orientation="Horizontal"> <Button Content="Salvar" Click="Button_Click_1"/> <Button Content="Cancelar" /> <Button Content="Excluir"/> </StackPanel> </Grid> </Window>

Essa funcionalidade de referenciar estilos pelo nome também se aplica a estilos locais, criados dentro de um container qualquer. É importante também observar que a utilização de um estilo específico sobrepõe estilos genéricos.

Temos nas Listagens 10 e 11 uma demonstração prática de uma situação real em que estilos nomeados podem ser utilizados. Nestes exemplos foram criados estilos que formatam os botões de acordo com sua função: Salvar, Cancelar ou Excluir.

Listagem 10. Criação de estilos para botões por função

<Style TargetType="Button" x:Key="estiloBotaoSalvar"> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="30"/> <Setter Property="BorderBrush" Value="Green"/> <Setter Property="Foreground" Value="Green"/> </Style> <Style TargetType="Button" x:Key="estiloBotaoCancelar"> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="30"/> <Setter Property="BorderBrush" Value="Orange"/> <Setter Property="Foreground" Value="Orange"/> </Style> <Style TargetType="Button" x:Key="estiloBotaoExcluir"> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="30"/> <Setter Property="BorderBrush" Value="Red"/> <Setter Property="Foreground" Value="Red"/> </Style>

Listagem 11. Aplicando cada estilo em um botão específico

<Button Content="Salvar" /> <Button Content="Cancelar" /> <Button Content="Excluir" />

A Figura 5 mostra a aparência final da tela, onde vemos alguns controles formatados individualmente através da utilização de estilos globais, porém nomeados.

Figura 5. Janela com controles formatados por estilos nomeados

Repare que nos três estilos criados na Listagem 10 algumas propriedades foram repetidas, pois como foi dito, o uso de um estilo específico sobrepõe estilos mais genéricos, portanto foi preciso redefinir a largura e altura dos botões. Como não é possível que um controle utilize dois estilos simultaneamente, referenciando-os pelo seu atributo Key, o WPF/XAML nos permite criar estilos baseados em outros, dos quais podem herdar algumas características já definidas. Neste exemplo, poderíamos ter um estilo genérico que definisse as dimensões dos botões, enquanto os demais formatam suas cores. Para criar estilos baseados em outros , utilizamos a propriedade BasedOn da tag Style, como mostra a Listagem 12.

Listagem 12. Criando estilos baseados em outros

<Style TargetType="Button" x:Key="estiloBotaoPadrao"> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="30"/> </Style> <Style TargetType="Button" x:Key="estiloBotaoSalvar" BasedOn="{StaticResource estiloBotaoPadrao}"> <Setter Property="BorderBrush" Value="Green"/> <Setter Property="Foreground" Value="Green"/> </Style> <Style TargetType="Button" x:Key="estiloBotaoCancelar"> <Setter Property="BorderBrush" Value="Orange"/> <Setter Property="Foreground" Value="Orange"/> </Style> <Style TargetType="Button" x:Key="estiloBotaoExcluir"> <Setter Property="BorderBrush" Value="Red"/> <Setter Property="Foreground" Value="Red"/> </Style>

Mais uma vez conseguimos reutilizar o código e facilitar manutenções futuras. Caso algum estilo específico precise alterar as propriedades de seu antecessor, isso pode ser feito normalmente apenas redefinindo o Setter em questão.

Todos os exemplos aqui foram desenvolvidos a partir de uma aplicação WPF, mas os conceitos são válidos também para aplicações Windows Store e Windows Phone, onde também se utiliza a XAML para a construção do layout. Esta linguagem é extremamente flexível e nos permite realizar praticamente todo tipo de customização sobre os controles de uma aplicação, tanto de forma individual quanto global.

Caso o leitor já tenha desenvolvido alguma aplicação com XAML e percebeu que algumas propriedades visuais estão sendo repetidas, pode ser essa uma boa oportunidade para aplicar os styles e deixar o código principal das janelas mais limpo e claro, delegando parte da formatação de interface para outras partes do código.

Artigos relacionados