Visão geral de XAML (WPF .NET)
Este artigo descreve os recursos da linguagem XAML e demonstra como você pode usar XAML para escrever aplicativos do WPF (Windows Presentation Foundation). Este artigo descreve especificamente o XAML, conforme implementado pelo WPF. O XAML em si é um conceito de linguagem maior que o WPF.
O que é XAML
O XAML é uma linguagem de marcação declarativa. Conforme aplicado ao modelo de programação do .NET, o XAML simplifica a criação de uma interface do usuário para um aplicativo .NET. Você pode criar elementos de interface do usuário visíveis na marcação XAML declarativa e separar a definição da interface do usuário da lógica de tempo de execução usando arquivos code-behind que são unidos à marcação por meio de definições parciais de classe. O XAML representa diretamente a instanciação de objetos em um conjunto específico de tipos de suporte definidos em assemblies. Isso é diferente da maioria das outras linguagens de marcação, que são geralmente uma linguagem interpretada sem um vínculo direto para um sistema de tipos de suporte. O XAML habilita um fluxo de trabalho em que partes separadas podem trabalhar na interface do usuário e na lógica de um aplicativo, usando ferramentas potencialmente diferentes.
Quando representados como texto, arquivos XAML são arquivos XML que geralmente tem a extensão .xaml
. Os arquivos podem ser codificados por qualquer codificação XML, mas a codificação como UTF-8 é a usual.
O exemplo a seguir mostra como você pode criar um botão como parte de uma interface do usuário. Este exemplo destina-se a fornecer uma amostra de como o XAML representa metáforas comuns de programação de interface do usuário (não é uma amostra completa).
<StackPanel>
<Button Content="Click Me"/>
</StackPanel>
Resumo sobre sintaxe XAML
As seções a seguir explicam as formas básicas de sintaxe XAML e fornecem um exemplo de marcação curta. Essas seções não se destinam a fornecer informações completas sobre cada formulário de sintaxe, como elas são representadas no sistema de tipos de backup. Para obter mais informações sobre as especificidades da sintaxe XAML, confira Resumo sobre a Sintaxe XAML.
Grande parte do material nas próximas seções será elementar para você caso tenha experiência anterior com a linguagem XML. Isso é consequência de um dos princípios básicos de design do XAML. A linguagem XAML define conceitos próprios, mas esses conceitos funcionam dentro da linguagem XML e do formulário de marcação.
Elementos do objeto XAML
Normalmente, um elemento de objeto declara uma instância de um tipo. Esse tipo é definido nos assemblies referenciados pela tecnologia que usa XAML como linguagem.
A sintaxe de elemento de objeto sempre começa com um colchete de abertura (<
). Isso é seguido pelo nome do tipo em que você deseja criar uma instância. (O nome pode incluir um prefixo, um conceito que será explicado posteriormente.) Depois disso, opcionalmente, você pode declarar atributos no elemento de objeto. Para concluir a marca de elemento de objeto, termine com um colchete de ângulo de fechamento (>
). Em vez disso, você pode usar um formulário de auto-fechamento que não tem nenhum conteúdo, completando a marca com uma barra para frente e um colchete de ângulo de fechamento em sucessão (/>
). Por exemplo, examine o snippet de marcação mostrado anteriormente novamente.
<StackPanel>
<Button Content="Click Me"/>
</StackPanel>
Isso especifica dois elementos de objeto: <StackPanel>
(com conteúdo e, posteriormente, uma marca de fechamento) e <Button .../>
(o formulário de autofechamento, com vários atributos). Os elementos de objeto StackPanel
e Button
cada um mapeia para o nome de uma classe que é definida pelo WPF e faz parte dos assemblies do WPF. Ao especificar uma marca de elemento de objeto, você cria uma instrução para o processamento XAML para criar uma nova instância do tipo subjacente. Cada instância é criada chamando o construtor sem parâmetros do tipo subjacente ao analisar e carregar o XAML.
Sintaxe de atributo (propriedades)
As propriedades de um objeto geralmente podem ser expressas como atributos do elemento de objeto. A sintaxe do atributo nomeia a propriedade do objeto que está sendo definida, seguida pelo operador de atribuição (=). O valor de um atributo é sempre especificado como uma cadeia de caracteres entre aspas.
A sintaxe de atributo é a sintaxe de configuração de propriedade mais simplificada e é a sintaxe mais intuitiva para os desenvolvedores que já usaram linguagens de marcação anteriormente. Por exemplo, a marcação a seguir cria um botão que tem texto vermelho e um plano de fundo azul com um texto de exibição de Content
.
<Button Background="Blue" Foreground="Red" Content="This is a button"/>
Sintaxe de elementos da propriedade
Para algumas propriedades de um elemento de objeto, a sintaxe de atributo não é possível, pois o objeto ou as informações necessárias para fornecer o valor da propriedade não podem ser expressas adequadamente dentro da aspa e restrições de cadeia de caracteres da sintaxe de atributo. Nesses casos é possível usar uma sintaxe diferente, conhecida como sintaxe de elemento de propriedade.
A sintaxe da marca inicial do elemento de propriedade é <TypeName.PropertyName>
. Geralmente, o conteúdo dessa marca é um elemento de objeto do tipo que a propriedade usa como seu valor. Depois de especificar o conteúdo, você deve fechar o elemento de propriedade com uma marca final. A sintaxe da marca final é </TypeName.PropertyName>
.
Se uma sintaxe de atributo é possível, usá-la é normalmente mais conveniente e permite uma marcação mais compacta, mas isso geralmente é apenas uma questão de estilo e não uma limitação técnica. O exemplo a seguir mostra as mesmas propriedades que foram definidas no exemplo de sintaxe de atributo anterior sendo definidas novamente, mas desta vez usando sintaxe de elemento de propriedade para todas as propriedades do Button
.
<Button>
<Button.Background>
<SolidColorBrush Color="Blue"/>
</Button.Background>
<Button.Foreground>
<SolidColorBrush Color="Red"/>
</Button.Foreground>
<Button.Content>
This is a button
</Button.Content>
</Button>
Sintaxe da coleção
A linguagem XAML inclui algumas otimizações que produzem uma marcação mais legível. Uma otimização desse tipo é tal que, se uma determinada propriedade utiliza um tipo de coleção, os itens que você declara na marcação como elementos filho no valor dessa propriedade tornam-se parte da coleção. Nesse caso, uma coleção de elementos de objeto filho é o valor que está sendo definido como a propriedade da coleção.
O exemplo a seguir mostra a sintaxe da coleção para definir valores da propriedade GradientStops.
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<!-- no explicit new GradientStopCollection, parser knows how to find or create -->
<GradientStop Offset="0.0" Color="Red" />
<GradientStop Offset="1.0" Color="Blue" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
Propriedades de conteúdo XAML
O XAML especifica um recurso de linguagem pelo qual uma classe pode designar exatamente uma de suas propriedades para ser a propriedade de conteúdo XAML. Elementos filho desse elemento de objeto são usados para definir o valor dessa propriedade de conteúdo. Em outras palavras, para a propriedade de conteúdo exclusivamente, você pode omitir um elemento de propriedade ao definir essa propriedade na marcação XAML e produzir uma metáfora de pai/filho mais visível na marcação.
Por exemplo, Border especifica uma propriedade de conteúdo de Child. Os dois elementos Border a seguir são tratados de forma idêntica. O primeiro aproveita a sintaxe de propriedade de conteúdo e omite o elemento de propriedade Border.Child
. O segundo mostra Border.Child
explicitamente.
<Border>
<TextBox Width="300"/>
</Border>
<!--explicit equivalent-->
<Border>
<Border.Child>
<TextBox Width="300"/>
</Border.Child>
</Border>
Como uma regra da linguagem XAML, o valor de uma propriedade de conteúdo XAML deve ser fornecido inteiramente antes ou inteiramente depois de todos os outros elementos de propriedade nesse elemento de objeto. Por exemplo, a marcação a seguir não é compilada.
<Button>I am a
<Button.Background>Blue</Button.Background>
blue button</Button>
Para obter mais informações sobre as especificidades da sintaxe XAML, confira Resumo sobre a Sintaxe XAML.
Conteúdo de texto
Um número pequeno de elementos XAML pode processar texto diretamente como seu conteúdo. Para habilitar isso, um dos casos a seguir deve ser verdadeiro:
A classe deve declarar uma propriedade de conteúdo e essa propriedade de conteúdo deve ser de um tipo atribuível a uma cadeia de caracteres (o tipo pode ser Object). Por exemplo, qualquer ContentControl usa Content como sua propriedade de conteúdo e seu tipo Object, e isso dá suporte ao seguinte uso em um ContentControl como um Button:
<Button>Hello</Button>
.O tipo deve declarar um conversor de tipo e, nesse caso, o conteúdo de texto é usado como texto de inicialização para esse conversor de tipo. Por exemplo,
<Brush>Blue</Brush>
converte o valor de conteúdo deBlue
em um pincel. Esse caso é menos comum na prática.O tipo deve ser um primitivo conhecido de linguagem XAML.
Propriedades de conteúdo e sintaxe de coleção combinadas
Considere este exemplo.
<StackPanel>
<Button>First Button</Button>
<Button>Second Button</Button>
</StackPanel>
Aqui, cada Button é um elemento filho de StackPanel. Isso é uma marcação otimizada e intuitiva que omite duas marcas por dois motivos diferentes.
Elemento de propriedade StackPanel.Children omitido:StackPanel deriva de Panel. Panel define Panel.Children como sua propriedade de conteúdo XAML.
Elemento de objeto UIElementCollection omitido: A propriedade Panel.Children usa o tipo UIElementCollection, que implementa IList. A marca de elemento da coleção pode ser omitida, com base nas regras XAML para processamento de coleções como IList. (Nesse caso, UIElementCollection não pode ser instanciado porque não expõe um construtor sem parâmetros e é por isso que o elemento objeto UIElementCollection é mostrado comentado).
<StackPanel>
<StackPanel.Children>
<!--<UIElementCollection>-->
<Button>First Button</Button>
<Button>Second Button</Button>
<!--</UIElementCollection>-->
</StackPanel.Children>
</StackPanel>
Sintaxe de atributo (eventos)
A sintaxe de atributo também pode ser usada para os membros que são eventos, em vez de propriedades. Nesse caso, o nome do atributo é o nome do evento. A implementação de WPF de eventos para XAML, o valor do atributo é o nome de um manipulador que implementa o delegado do evento. Por exemplo, a marcação a seguir atribui um manipulador para o evento Click a um Button criado na marcação:
<Button Click="Button_Click" >Click Me!</Button>
Há mais em eventos e XAML no WPF do que apenas este exemplo da sintaxe de atributo. Por exemplo, você pode se perguntar o que o ClickHandler
referenciado aqui representa e como ele é definido. Isso será explicado na próxima seção code-behind de Eventos e XAML deste artigo.
Maiúsculas e minúsculas e espaço em branco no XAML
Em geral, o XAML diferencia maiúsculas de minúsculas. Para fins de resolução de tipos de backup, o XAML do WPF diferencia maiúsculas de minúsculas pelas mesmas regras que o CLR diferencia maiúsculas de minúsculas. Elementos de objeto, elementos de propriedade e nomes de atributo devem ser especificados usando diferenciação de maiúsculas e minúsculas quando comparados pelo nome ao tipo subjacente no assembly ou a um membro de um tipo. As palavras-chave e os primitivos da linguagem XAML também diferenciam maiúsculas de minúsculas. Os valores nem sempre diferenciam maiúsculas de minúsculas. A diferenciação de maiúsculas e minúsculas em valores dependerá do comportamento do conversor de tipo associado com a propriedade que recebe o valor ou do tipo de valor da propriedade. Por exemplo, as propriedades que tomam o tipo Boolean podem usar true
ou True
como valores equivalentes, mas apenas porque a conversão de tipo de analisador XAML do WPF nativo para cadeia de caracteres para Boolean já permite isso como equivalentes.
Os processadores XAML do WPF e serializadores ignorarão ou removerão todo o espaço em branco não insignificante e normalizarão qualquer espaço em branco significativo. Isso é consistente com as recomendações padrão de comportamento de espaço em branco da especificação XAML. Esse comportamento só é consequência quando você especifica cadeias de caracteres dentro das propriedades de conteúdo XAML. Em termos mais simples, o XAML converte caracteres de espaço, de linha e de tabulação em espaços e preserva um espaço se encontrado em qualquer extremidade de uma cadeia de caracteres contígua. A explicação completa do tratamento de espaço em branco XAML não é abordada neste artigo. Para obter mais informações, confira Processamento de espaço em branco no XAML.
Extensões de marcação
Extensões de marcação são um conceito de linguagem XAML. Quando usados para fornecer o valor de uma sintaxe de atributo, chaves ({
e }
) indicam um uso de extensão de marcação. Esse uso direciona o processamento XAML para escape do tratamento geral de valores de atributo como uma cadeia de caracteres literal ou um valor conversível em cadeia de caracteres.
As extensões de marcação mais comuns usadas na programação de aplicativos do WPF são Binding
, usadas para expressões de associação de dados e as referências de recurso StaticResource
e DynamicResource
. Ao usar extensões de marcação, você pode utilizar a sintaxe de atributo para fornecer valores para propriedades, mesmo que essa propriedade não dê suporte a uma sintaxe de atributo em geral. As extensões de marcação geralmente usam tipos de expressão intermediária para habilitar recursos como adiar valores ou referenciar outros objetos que só estão presentes em tempo de execução.
Por exemplo, a marcação a seguir define o valor da propriedade Style usando a sintaxe de atributo. A propriedade Style usa uma instância da classe Style, que por padrão não pôde ser instanciada por uma cadeia de caracteres de sintaxe de atributo. Mas, nesse caso, o atributo faz referência a uma extensão de marcação específica, StaticResource
. Quando essa extensão de marcação é processada, ela retorna uma referência a um estilo que foi anteriormente instanciado como um recurso com chave em um dicionário de recursos.
<Window x:Class="index.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="100" Width="300">
<Window.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Gold"/>
<Style TargetType="Border" x:Key="PageBackground">
<Setter Property="BorderBrush" Value="Blue"/>
<Setter Property="BorderThickness" Value="5" />
</Style>
</Window.Resources>
<Border Style="{StaticResource PageBackground}">
<StackPanel>
<TextBlock Text="Hello" />
</StackPanel>
</Border>
</Window>
Para uma referência listando todas as extensões de marcação para XAML implementado especificamente no WPF, consulte Extensões XAML WPF. Para obter uma lista de referência das extensões de marcação definidas por System.Xaml e que estão mais amplamente disponíveis para implementações XAML do .NET, consulte Recursos de linguagem de namespace XAML (x:). Para obter mais informações sobre conceitos de extensão de marcação, consulte Extensões de marcação e XAML WPF.
Conversores de tipo
Na seção Resumo sobre sintaxe XAML, foi mencionado que o valor do atributo deve poder ser definido por uma cadeia de caracteres. O tratamento básico e nativo de como as cadeias de caracteres são convertidas em outros tipos de objeto ou valores primitivos baseia-se no tipo String em si, juntamente com o processamento nativo para determinados tipos, como DateTime ou Uri. Mas muitos tipos de WPF ou membros desses tipos estendem o comportamento básico de processamento de atributo de cadeia de caracteres de forma que instâncias de tipos de objeto mais complexos possam ser especificadas como cadeias de caracteres e atributos.
A estrutura Thickness é um exemplo de um tipo que tem uma conversão de tipo habilitada para usos XAML. Thickness indica medidas dentro de um retângulo aninhado e é usado como o valor para propriedades como Margin. Ao colocar um conversor de tipo em Thickness, todas as propriedades que usam um Thickness são mais fáceis de especificar no XAML porque podem ser especificadas como atributos. O exemplo a seguir usa uma sintaxe de conversão de tipo e atributo para fornecer um valor para um Margin:
<Button Margin="10,20,10,30" Content="Click me"/>
O exemplo de sintaxe de atributo anterior é equivalente ao exemplo de sintaxe mais detalhado a seguir, em que o Margin é definido por meio da sintaxe do elemento de propriedade que contém um elemento de objeto Thickness. As quatro propriedades principais de Thickness são definidas como atributos na nova instância:
<Button Content="Click me">
<Button.Margin>
<Thickness Left="10" Top="20" Right="10" Bottom="30"/>
</Button.Margin>
</Button>
Observação
Há também um número limitado de objetos em que a conversão de tipo é a única maneira pública de definir uma propriedade para esse tipo sem envolver uma subclasse, porque o próprio tipo não tem um construtor sem parâmetros. Um exemplo é Cursor.
Para obter mais informações sobre conversão de tipo, confira TypeConverters e XAML.
Elementos raiz e namespaces
Um arquivo XAML deve ter apenas um elemento raiz, para ser um arquivo XML bem formado e um arquivo XAML válido. Para cenários típicos do WPF, você usa um elemento raiz que tem um significado proeminente no modelo de aplicativo do WPF (por exemplo, Window ou Page para uma página, ResourceDictionary para um dicionário externo ou Application para a definição do aplicativo). O exemplo a seguir mostra o elemento raiz de um arquivo XAML típico para uma página do WPF, com o elemento raiz de Page.
<Page x:Class="index.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page1">
</Page>
O elemento raiz também contém os atributos xmlns
e xmlns:x
. Esses atributos indicam a um processador XAML quais namespaces XAML contêm as definições de tipo para tipos de suporte que a marcação referenciará como elementos. O atributo xmlns
indica especificamente o namespace XAML padrão. Dentro do namespace XAML padrão, os elementos de objeto na marcação podem ser especificados sem um prefixo. Para a maioria dos cenários de aplicativo WPF e para quase todos os exemplos fornecidos nas seções do WPF do SDK, o namespace XAML padrão é mapeado para o namespace do WPF http://schemas.microsoft.com/winfx/2006/xaml/presentation
. O atributo xmlns:x
indica um namespace XAML adicional, que mapeia o namespace http://schemas.microsoft.com/winfx/2006/xaml
da linguagem XAML.
Esse uso de xmlns
para definir um escopo para uso e mapeamento de um namescope é consistente com a especificação do XML 1.0. Namescopes XAML são diferentes de namescopes XML apenas porque um namescope XAML também implica algo sobre como os elementos do namescope têm suporte de tipos quando se trata de resolução de tipo e análise do XAML.
Os atributos xmlns
são estritamente necessários no elemento raiz de cada arquivo XAML. As definições do xmlns
serão aplicadas a todos os elementos descendentes do elemento raiz (esse comportamento é novamente consistente com a especificação XML 1.0 para xmlns
). Atributos xmlns
também são permitidos em outros elementos abaixo da raiz e se aplicariam a quaisquer elementos descendentes do elemento definidor. No entanto, definição ou redefinição frequente de namespaces XAML pode resultar em um estilo de marcação XAML difícil de ler.
A implementação do WPF de seu processador XAML inclui uma infraestrutura que tem reconhecimento dos assemblies principais do WPF. Os assemblies principais do WPF são conhecidos por conter os tipos que dão suporte aos mapeamentos do WPF para o namespace XAML padrão. Isso é habilitado por meio da configuração que faz parte de seu arquivo de build do projeto e dos sistemas de projeto e build do WPF. Portanto, declarar o namespace XAML padrão como o xmlns
padrão é tudo o que é necessário para referenciar elementos XAML provenientes de assemblies WPF.
O prefixo x:
No exemplo de elemento raiz anterior, o prefixo x:
foi usado para mapear o namespace XAML http://schemas.microsoft.com/winfx/2006/xaml
, que é o namespace XAML dedicado que dá suporte a constructos de linguagem XAML. Esse prefixo x:
é usado para mapear esse namespace XAML nos modelos para projetos, em exemplos, e na documentação em todo esse SDK. O namespace XAML para a linguagem XAML contém vários constructos de programação que você usará com frequência em seu XAML. A seguir está uma lista dos constructos de programação de prefixo x:
mais comuns que você usará:
x:Key: define uma chave exclusiva para cada recurso em um ResourceDictionary (ou conceitos de dicionário semelhantes em outras estruturas).
x:Key
provavelmente será a responsável por 90% dos usos dex:
que você verá na marcação de um aplicativo WPF típico.x:Class: especifica o namespace CLR e o nome da classe para a classe que fornece code-behind para uma página XAML. Você deve ter uma classe desse tipo para dar suporte a code-behind segundo o modelo de programação do WPF e, portanto, você quase sempre vê
x:
mapeado, mesmo que não existam recursos.X:Name: especifica um nome de objeto de tempo de execução para a instância que existe em código de tempo de execução após o processamento de um elemento de objeto. Em geral, você usará frequentemente uma propriedade equivalente definida pelo WPF para X:Name. Essas propriedades são mapeadas especificamente para uma propriedade de suporte CLR e, portanto, são mais convenientes para programação de aplicativos, em que você usa frequentemente o código em tempo de execução para localizar os elementos nomeados do XAML inicializado. A propriedade mais comum desse tipo é FrameworkElement.Name. Você ainda poderá usar x:Name quando a propriedade de Name nível de estrutura do WPF equivalente não tiver suporte em um tipo específico. Isso ocorre em determinados cenários de animação.
x:Static: habilita uma referência que retorna um valor estático que, de outra forma, não é uma propriedade compatível com XAML.
x:Type: constrói uma referência de Type com base em um nome de tipo. Isso é usado para especificar atributos que levam Type, como Style.TargetType, embora frequentemente a propriedade tenha conversão de cadeia de caracteres nativa paraType de forma que o uso da extensão de marcação x:Type seja opcional.
Há construções de programação adicionais no x:
namespace prefixo/XAML, que não são tão comuns. Para obter detalhes, consulte Recursos de linguagem (x:) do namespace XAML.
Prefixos personalizados e tipos personalizados
Para seus próprios assemblies personalizados ou para assemblies fora do núcleo WPF de PresentationCore, PresentationFramework e WindowsBase, você pode especificar o assembly como parte de um mapeamento de xmlns
personalizado. Você pode então referenciar tipos desse assembly em seu XAML, desde que esse tipo esteja corretamente implementado para dar suporte aos usos XAML que você está tentando executar.
Veja a seguir um exemplo básico de como os prefixos personalizados funcionam na marcação XAML. O prefixo custom
é definido na marca de elemento raiz e mapeado para um assembly específico que está empacotado e disponível com o aplicativo. Esse assembly contém um tipo NumericUpDown
, que é implementado para dar suporte ao uso geral do XAML, bem como usar uma herança de classe que permite sua inserção, nesse momento específico, em um modelo de conteúdo XAML WPF. Uma instância desse controle NumericUpDown
é declarada como um elemento de objeto, usando o prefixo para que um analisador XAML saiba qual namespace XAML contém o tipo e, portanto, a localização do assembly de suporte que contém a definição de tipo.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:NumericUpDownCustomControl;assembly=CustomLibrary"
>
<StackPanel Name="LayoutRoot">
<custom:NumericUpDown Name="numericCtrl1" Width="100" Height="60"/>
...
</StackPanel>
</Page>
Para obter mais informações sobre tipos personalizados em XAML, consulte XAML e classes personalizadas para WPF.
Para obter mais informações sobre como namespaces XML e namespaces de código em assemblies estão relacionados, confira Namespaces XAML e Mapeamento de Namespace para WPF XAML.
Eventos e code-behind XAML
A maioria dos aplicativos WPF consiste em marcação XAML e code-behind. Em um projeto, o XAML é escrito como um arquivo .xaml
e uma linguagem CLR, como Microsoft Visual Basic ou C#, é usada para escrever um arquivo code-behind. Quando um arquivo XAML é compilado com marcação como parte dos modelos de aplicativos e programação WPF, o local do arquivo XAML code-behind para um arquivo XAML é identificado por meio da especificação de um namespace e classe como o atributo x:Class
do elemento raiz do XAML.
Nos exemplos até agora você viu vários botões, mas nenhum deles tinha ainda qualquer comportamento lógico associado. O mecanismo principal no nível do aplicativo para adicionar um comportamento para um elemento de objeto é usar um evento existente da classe de elemento e gravar um manipulador específico para esse evento que é invocado quando esse evento é gerado em tempo de execução. O nome do evento e o nome do manipulador a usar são especificados na marcação, enquanto o código que implementa o manipulador é definido no code-behind.
<Page x:Class="ExampleNamespace.ExamplePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Click="Button_Click">Click me</Button>
</StackPanel>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ExampleNamespace;
public partial class ExamplePage : Page
{
public ExamplePage() =>
InitializeComponent();
private void Button_Click(object sender, RoutedEventArgs e)
{
var buttonControl = (Button)e.Source;
buttonControl.Foreground = Brushes.Red;
}
}
Class ExamplePage
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim buttonControl = DirectCast(e.Source, Button)
buttonControl.Foreground = Brushes.Red
End Sub
End Class
Observe que o arquivo code-behind usa o namespace ExampleNamespace
CLR (o namespace não está visível no Visual Basic) e declara ExamplePage
como uma classe parcial dentro desse namespace. Isso é paralelo ao x:Class
valor do atributo de ExampleNamespace
.ExamplePage
que foi fornecida na raiz de marcação. O compilador de marcação do WPF criará uma classe parcial para qualquer arquivo XAML compilado, derivando uma classe do tipo de elemento raiz. Quando você fornece code-behind que também define a mesma classe parcial, o código resultante é combinado dentro do mesmo namespace e classe do aplicativo compilado.
Importante
No Visual Basic, o namespace raiz está implícito para o XAML e o code-behind. Somente namespaces aninhados são visíveis. Este artigo demonstra o XAML do projeto C#.
Para obter mais informações sobre os requisitos de programação code-behind no WPF, confira Requisitos de Code-behind, Manipulador de Eventos e Classe Parcial no WPF.
Se você não quiser criar um arquivo code-behind separado, também poderá inserir seu código em um arquivo XAML. No entanto, o código embutido é uma técnica menos versátil que tem limitações significativas. Para obter mais informações, confira Code-Behind e XAML no WPF.
Eventos roteados
Um recurso de evento específico que é fundamental para o WPF é um evento roteado. Eventos roteados habilitam um elemento para manipular um evento que foi acionado por um elemento diferente, desde que os elementos estejam conectados por meio de uma relação de árvore. Ao especificar o tratamento de eventos com um atributo XAML, o evento roteado pode ser escutado e manipulado em qualquer elemento, incluindo elementos que não listam esse evento específico na tabela de membros da classe. Isso é feito qualificando o atributo de nome do evento com o nome de classe proprietária. Por exemplo, o StackPanel
pai no exemplo de StackPanel
/ Button
em andamento pode registrar um manipulador para o evento Click do botão de elemento filho especificando o atributo Button.Click
no elemento de objeto StackPanel
, com o nome do manipulador como o valor do atributo. Para obter mais informações, consulte Visão geral de eventos roteados.
Elementos nomeados
Por padrão, a instância de objeto criada em um grafo de objeto processando um elemento de objeto XAML não tem um identificador exclusivo ou uma referência de objeto. Por outro lado, se você chamar um construtor no código, você quase sempre usará o resultado do construtor para definir uma variável para a instância construída, de modo que você poderá fazer referência à instância posteriormente no seu código. Para fornecer acesso padronizado a objetos que foram criados por meio de uma definição de marcação, o XAML define o atributo x:Name. Você pode definir o valor do atributo x:Name
em qualquer elemento de objeto. Em seu code-behind, o identificador escolhido é equivalente a uma variável de instância que faz referência à instância construída. Em todos os aspectos, os elementos nomeados funcionam como se fossem instâncias de objeto (o nome faz referência a essa instância) e o code-behind pode referenciar os elementos nomeados para lidar com interações em tempo de execução dentro do aplicativo. Essa conexão entre instâncias e variáveis é realizada pelo compilador de marcação XAML do WPF e envolve mais especificamente recursos e padrões, como InitializeComponent que não serão discutidos em detalhes neste artigo.
Os elementos XAML no nível da estrutura do WPF herdam uma propriedade Name, que é equivalente ao atributo de x:Name
definido por XAML. Algumas outras classes também fornecem equivalentes de nível de propriedade para x:Name
, que também geralmente é definido como uma propriedade Name
. Em geral, se você não conseguir encontrar uma propriedade Name
na tabela de membros para o elemento/tipo escolhido, use x:Name
em vez disso. Os valores x:Name
fornecerão um identificador para um elemento XAML que pode ser usado em tempo de execução, seja por subsistemas específicos ou por métodos utilitários, como FindName.
O exemplo a seguir define Name em um elemento StackPanel. Um manipulador em um Button dentro desse StackPanel faz referência ao StackPanel por meio de seu buttonContainer
de referência de instância, conforme definido por Name.
<StackPanel Name="buttonContainer">
<Button Click="RemoveThis_Click">Click to remove this button</Button>
</StackPanel>
private void RemoveThis_Click(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)e.Source;
if (buttonContainer.Children.Contains(element))
buttonContainer.Children.Remove(element);
}
Private Sub RemoveThis_Click(sender As Object, e As RoutedEventArgs)
Dim element = DirectCast(e.Source, FrameworkElement)
If buttonContainer.Children.Contains(element) Then
buttonContainer.Children.Remove(element)
End If
End Sub
Assim como uma variável, o nome XAML para uma instância é regido por um conceito do escopo, de modo que pode ser imposto que os nomes sejam exclusivos dentro de um determinado escopo previsível. A marcação primária que define uma página denota um namescope XAML exclusivo, com o limite de namescopes XAML sendo o elemento raiz dessa página. No entanto, outras fontes de marcação podem interagir com uma página em tempo de execução, como estilos ou modelos dentro de estilos, e essas fontes de marcação geralmente têm seus próprios namescopes XAML que não necessariamente se conectam com o namescope XAML da página. Para obter mais informações sobre os namescopes x:Name
e XAML, confira Name, diretiva x:Name ou Namescopes XAML do WPF.
Propriedades anexadas e eventos anexados
O XAML especifica um recurso de linguagem que permite que determinadas propriedades ou eventos sejam especificados em qualquer elemento, mesmo que a propriedade ou o evento não exista nas definições do tipo para o elemento em que está sendo definido. A versão de propriedades desse recurso é chamada de uma propriedade anexada, a versão de eventos é chamada de um evento anexado. Conceitualmente, você pode pensar em propriedades anexadas e eventos anexados como membros globais que podem ser definidos em qualquer instância de objeto/elemento XAML. No entanto, esse elemento/classe ou uma infraestrutura maior deve dar suporte a um repositório de propriedades de suporte para os valores anexados.
Propriedades anexadas em XAML normalmente são usadas por meio da sintaxe de atributo. Na sintaxe do atributo, especifique uma propriedade anexada no formulário ownerType.propertyName
.
Superficialmente, isso se assemelha a um uso de elemento de propriedade, mas nesse caso o ownerType
que você especificar é sempre um tipo diferente do elemento de objeto em que a propriedade anexada está sendo definida. ownerType
é o tipo que fornece os métodos acessadores exigidos por um processador XAML para obter ou definir o valor da propriedade anexada.
O cenário mais comum para propriedades anexadas é habilitar que elementos filho relatem um valor da propriedade para o respectivo elemento pai.
O exemplo a seguir ilustra a propriedade anexada DockPanel.Dock. A classe DockPanel define os acessadores para DockPanel.Dock e possui a propriedade anexada. A classe DockPanel também inclui lógica que itera seus elementos filho e verifica especificamente cada elemento quanto a um valor definido de DockPanel.Dock. Se um valor for encontrado, esse valor será usado durante o layout para posicionar os elementos filho. O uso da propriedade anexada DockPanel.Dock e essa funcionalidade de posicionamento é, na verdade, o cenário motivador para a classe DockPanel.
<DockPanel>
<Button DockPanel.Dock="Left" Width="100" Height="20">I am on the left</Button>
<Button DockPanel.Dock="Right" Width="100" Height="20">I am on the right</Button>
</DockPanel>
No WPF, a maioria ou todas as propriedades anexadas também são implementadas como propriedades de dependência. Para obter mais informações, consulte Visão geral das propriedades anexadas.
Os eventos anexados usam uma forma ownerType.eventName
semelhante de sintaxe de atributo. Assim como nos eventos não anexados, o valor do atributo para um evento anexado em XAML especifica o nome do método manipulador que é invocado quando o evento é manipulado no elemento. Usos de evento anexado no XAML WPF são menos comuns. Para obter mais informações, consulte Visão geral de eventos anexados.
Tipos de base
O XAML do WPF subjacente e seu namespace XAML são uma coleção de tipos que correspondem a objetos CLR e elementos de marcação para XAML. No entanto, nem todas as classes podem ser mapeadas para elementos. Classes abstratas, como ButtonBase e determinadas classes base não abstratas, são usadas para herança no modelo de objetos CLR. Classes base, incluindo aquelas abstratas, são importantes para o desenvolvimento de XAML, porque cada um dos elementos XAML concretos herda os membros de alguma classe base na sua hierarquia. Frequentemente esses membros incluem propriedades que podem ser definidas como atributos no elemento ou eventos que podem ser manipulados. FrameworkElement é a classe de interface do usuário base concreta do WPF no nível da estrutura do WPF. Ao projetar a interface do usuário, você usará várias classes de forma, painel, decorador ou controle, que derivam de FrameworkElement. Uma classe base relacionada, FrameworkContentElement, dá suporte a elementos orientados a documentos que funcionam bem para uma apresentação de layout de fluxo, usando APIs que espelham deliberadamente as APIs em FrameworkElement. A combinação de atributos no nível do elemento e um modelo de objeto CLR fornece um conjunto de propriedades comuns que são configuráveis na maioria dos elementos XAML concretos, independentemente do elemento XAML específico e seu tipo subjacente.
Segurança
XAML é uma linguagem de marcação que representa diretamente a instanciação e execução de objetos. É por isso que os elementos criados no XAML têm a mesma capacidade de interagir com os recursos do sistema (acesso à rede, E/S do sistema de arquivos, por exemplo) como o código do aplicativo. O XAML também tem o mesmo acesso aos recursos do sistema que o aplicativo de hospedagem.
CAS (Segurança de Acesso ao Código) no WPF
Ao contrário do .NET Framework, o WPF para .NET não dá suporte ao CAS. Para obter mais informações, confira as diferenças de segurança de acesso ao código.
Carregar XAML do código
O XAML pode ser usado para definir toda a interface do usuário, mas às vezes também é apropriado definir apenas uma parte da interface do usuário em XAML. Essa funcionalidade pode ser usada para:
- Habilite a personalização parcial.
- Armazenamento local de informações da interface do usuário.
- Modele um objeto de negócios.
A chave para esses cenários é a classe XamlReader e seu método Load. A entrada é um arquivo XAML e a saída é um objeto que representa toda a árvore de objetos em tempo de execução que foi criada com base nessa marcação. Você pode inserir o objeto para ser uma propriedade de outro objeto que já existe no aplicativo. Enquanto a propriedade estiver no modelo de conteúdo e tiver recursos de exibição que notificarão o mecanismo de execução de que o novo conteúdo foi adicionado ao aplicativo, você poderá modificar o conteúdo de um aplicativo em execução facilmente carregando dinamicamente no XAML.
Confira também
.NET Desktop feedback