Visão geral dos recursos XAML (WPF .NET)

Um recurso é um objeto que pode ser reutilizado em diferentes locais do seu aplicativo. Exemplos de recursos incluem pincéis e estilos. Esta visão geral descreve como usar recursos em XAML (Extensible Application Markup Language). Você também pode criar e acessar recursos usando código.

Observação

Os recursos XAML descritos neste artigo são diferentes dos recursos do aplicativo, que geralmente são arquivos adicionados a um aplicativo, como conteúdo, dados ou arquivos inseridos.

Usar recursos em XAML

O exemplo a seguir define a SolidColorBrush como um recurso no elemento raiz de uma página. Em seguida, o exemplo faz referência ao recurso e o usa para definir propriedades de vários elementos filho, incluindo um Ellipse, um TextBlocke um Button.

<Window x:Class="resources.ResExample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ResExample" Height="400" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="#05E0E9"/>
        <Style TargetType="Border">
            <Setter Property="Background" Value="#4E1A3D" />
            <Setter Property="BorderThickness" Value="5" />
            <Setter Property="BorderBrush">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Offset="0.0" Color="#4E1A3D"/>
                        <GradientStop Offset="1.0" Color="Salmon"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="TextBlock" x:Key="TitleText">
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="Foreground" Value="#4E87D4"/>
            <Setter Property="FontFamily" Value="Trebuchet MS"/>
            <Setter Property="Margin" Value="0,10,10,10"/>
        </Style>
        <Style TargetType="TextBlock" x:Key="Label">
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
            <Setter Property="FontFamily" Value="Arial"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Margin" Value="0,3,10,0"/>
        </Style>
    </Window.Resources>

    <Border>
        <StackPanel>
            <TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
            <TextBlock Style="{StaticResource Label}">Label</TextBlock>
            <TextBlock HorizontalAlignment="Right" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
            <Button HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
            <Ellipse HorizontalAlignment="Center" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="10" />
        </StackPanel>
    </Border>

</Window>

Cada elemento de nível de estrutura (FrameworkElement ou FrameworkContentElement) tem uma Resources propriedade, que é um ResourceDictionary tipo que contém recursos definidos. Você pode definir recursos em qualquer elemento, como um Button. No entanto, os recursos são mais frequentemente definidos no elemento raiz, que está Window no exemplo.

Cada recurso em um dicionário de recursos deve ter uma chave exclusiva. Ao definir recursos na marcação, atribua a chave exclusiva por meio da Diretiva x:Key. Normalmente, a chave é uma cadeia de caracteres; no entanto, você pode também a definir com outros tipos de objeto usando as extensões de marcação apropriadas. Chaves não cadeia de caracteres para recursos são usadas por determinadas áreas de recursos no WPF, principalmente para estilos, recursos de componente e estilo de dados.

Você pode usar um recurso definido com a sintaxe de extensão de marcação de recurso que especifica o nome da chave do recurso. Por exemplo, use o recurso como o valor de uma propriedade em outro elemento.

<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>

No exemplo anterior, quando o carregador XAML processa o valor {StaticResource MyBrush} da propriedade em Button, a lógica de pesquisa de recursos primeiro verifica o dicionário de recursos para o Button elementoBackground. Se Button não tiver uma definição da chave MyBrush de recurso (nesse exemplo, não tem; sua coleção de recursos está vazia), a pesquisa verificará o elemento pai de Button. Se o recurso não estiver definido no pai, ele continuará a verificar a árvore lógica do objeto para cima até ser encontrado.

Se você definir recursos no elemento raiz, todos os elementos na árvore lógica, como o Window ou Page, poderão acessá-lo. E você pode reutilizar o mesmo recurso para definir o valor de qualquer propriedade que aceite o mesmo tipo que o recurso representa. No exemplo anterior, o mesmo MyBrush recurso define duas propriedades diferentes: Button.Background e Ellipse.Fill.

Recursos estáticos e dinâmicos

Um recurso pode ser referenciado como estático ou dinâmico. As referências são criadas usando a Extensão de Marcação StaticResource ou a Extensão de Marcação DynamicResource. Uma extensão de marcação é um recurso XAML que permite especificar uma referência de objeto fazendo com que a extensão de marcação processe a cadeia de caracteres de atributo e retorne o objeto para um carregador XAML. Para obter mais informações sobre o comportamento da extensão de marcação, consulte Extensões de marcação e o XAML do WPF.

Ao usar uma extensão de marcação, você normalmente fornece um ou mais parâmetros em forma de cadeia de caracteres que são processados por essa extensão de marcação específica. A Extensão de marcação de StaticResource processa uma chave procurando o valor dessa chave em todos os dicionários de recursos disponíveis. O processamento ocorre durante o carregamento, que é quando o processo de carregamento precisa atribuir o valor da propriedade. Em vez disso, a Extensão de Marcação DynamicResource processa uma chave criando uma expressão, e essa expressão permanece não avaliada até que o aplicativo seja executado, momento em que a expressão é avaliada para fornecer um valor.

Quando você referencia um recurso, as seguintes considerações podem influenciar se uma referência de recurso estático ou dinâmico será usada:

  • Ao determinar o design geral de como você cria os recursos para seu aplicativo (por página, no aplicativo, em XAML flexível ou em um assembly somente de recurso), considere o seguinte:

  • A funcionalidade do aplicativo. A atualização de recursos em tempo real faz parte dos requisitos do seu aplicativo?

  • O respectivo comportamento de pesquisa desse tipo de referência de recurso.

  • A propriedade ou o tipo de recurso específico e o comportamento nativo desses tipos.

Recursos estáticos

As referências de recursos estáticos funcionam melhor nas seguintes circunstâncias:

  • O design do aplicativo concentra a maior parte de seus recursos em dicionários de recursos no nível da página ou do aplicativo.

    As referências de recursos estáticos não são reavaliadas com base em comportamentos de tempo de execução, como recarregar uma página. Portanto, pode haver algum benefício de desempenho em evitar um grande número de referências de recursos dinâmicos quando elas não são necessárias com base no design do recurso e do aplicativo.

  • Você está definindo o valor de uma propriedade que não está em um ou em um DependencyObject Freezable.

  • Você está criando um dicionário de recursos compilado em uma DLL compartilhada entre aplicativos.

  • Você está criando um tema para um controle personalizado e está definindo recursos que são usados dentro dos temas.

    Para esse caso, você normalmente não deseja o comportamento de pesquisa de referência de recurso dinâmico. Em vez disso, use o comportamento de referência de recurso estático para que a pesquisa seja previsível e independente para o tema. Com uma referência de recurso dinâmico, até mesmo uma referência dentro de um tema é deixada sem avaliação até o tempo de execução. E há uma chance de que, quando o tema for aplicado, algum elemento local redefina uma chave que seu tema está tentando referenciar, e o elemento local caia antes do próprio tema na pesquisa. Se isso acontecer, seu tema não se comportará conforme o esperado.

  • Você está usando recursos para definir um grande número de propriedades de dependência. As propriedades de dependência têm cache de valor efetivo conforme habilitado pelo sistema de propriedades, portanto, se você fornecer um valor para uma propriedade de dependência que possa ser avaliada no momento do carregamento, a propriedade de dependência não precisará verificar se há uma expressão reavaliada e poderá retornar o último valor efetivo. Essa técnica pode ser uma vantagem de desempenho.

  • Você deseja alterar o recurso subjacente para todos os consumidores ou manter instâncias graváveis separadas para cada consumidor usando o Atributo x:Shared.

Comportamento de pesquisa de recursos estáticos

O seguinte descreve o processo de pesquisa que ocorre automaticamente quando um recurso estático é referenciado por uma propriedade ou elemento:

  1. O processo de pesquisa verifica a chave solicitada no dicionário de recursos definido pelo elemento que define a propriedade.

  2. Em seguida, o processo de pesquisa percorre a árvore lógica para cima até o elemento pai e seu dicionário de recursos. Esse processo continua até que o elemento raiz seja alcançado.

  3. Os recursos do aplicativo são verificados. Os recursos do aplicativo são aqueles recursos dentro do dicionário de recursos definidos pelo Application objeto do aplicativo WPF.

As referências de recursos estáticos em um dicionário de recursos devem referenciar um recurso que já foi definido lexicalmente antes da referência ao recurso. As referências de encaminhamento não podem ser resolvidas por uma referência de recursos estáticos. Por esse motivo, projete sua estrutura de dicionário de recursos de forma que os recursos sejam definidos no início ou próximo ao início de cada dicionário de recursos respectivo.

A pesquisa de recursos estáticos pode se estender para temas ou recursos do sistema, mas essa pesquisa tem suporte apenas porque o carregador XAML adia a solicitação. O adiamento é necessário para que o tema de tempo de execução no momento em que a página é carregada se aplique corretamente ao aplicativo. No entanto, as referências de recursos estáticos a chaves que são conhecidas por existirem apenas em temas ou como recursos do sistema não são recomendadas, pois essas referências não serão reavaliadas se o tema for alterado pelo usuário em tempo real. Uma referência de recurso dinâmico é mais confiável quando você solicita recursos do sistema ou de tema. A exceção é quando um próprio elemento de tema solicita outro recurso. Essas referências devem ser referências de recursos estáticos, pelas razões mencionadas anteriormente.

O comportamento da exceção se uma referência de recurso estático não for encontrada varia. Se o recurso foi adiado, a exceção ocorre em runtime. Se o recurso não tiver sido adiado, a exceção ocorrerá no momento do carregamento.

Recursos dinâmicos

Os recursos dinâmicos funcionam melhor quando:

  • O valor do recurso, incluindo recursos do sistema ou recursos que podem ser definidos pelo usuário, depende de condições que não são conhecidas até o tempo de execução. Por exemplo, você pode criar valores de setter que se referem às propriedades do sistema conforme exposto por SystemColors, SystemFonts, ou SystemParameters. Esses valores são realmente dinâmicos, pois, basicamente, são obtidos do ambiente em runtime do usuário e do sistema operacional. Você também pode ter temas em nível do aplicativo que podem ser alterados, em que o acesso aos recursos em nível de página também deve captar a alteração.

  • Você está criando ou referenciando estilos de tema para um controle personalizado.

  • Você pretende ajustar o conteúdo de um ResourceDictionary durante o tempo de vida de um aplicativo.

  • Você tem uma estrutura de recursos complicada que tem interdependências, em que uma referência de encaminhamento pode ser necessária. As referências de recursos estáticos não dão suporte a referências diretas, mas as referências dinâmicas de recursos dão suporte a elas porque o recurso não precisa ser avaliado até o tempo de execução e, portanto, as referências diretas não são um conceito relevante.

  • Você está fazendo referência a um recurso que é grande da perspectiva de uma compilação ou conjunto de trabalho, e o recurso pode não ser usado imediatamente quando a página é carregada. As referências de recursos estáticos sempre são carregadas do XAML quando a página é carregada. No entanto, uma referência de recurso dinâmico não é carregada até que seja usada.

  • Você está criando um estilo em que os valores setter podem vir de outros valores influenciados por temas ou outras configurações do usuário.

  • Você está aplicando recursos a elementos que podem ser reassociados na árvore lógica durante o tempo de vida do aplicativo. Potencialmente, a alteração do pai também altera o escopo de pesquisa de recursos; portanto, se você desejar que o recurso de um elemento reassociado seja reavaliado com base no novo escopo, sempre use uma referência de recursos dinâmicos.

Comportamento de pesquisa de recursos dinâmicos

O comportamento de pesquisa de recursos para uma referência de recurso dinâmico é paralelo ao comportamento de pesquisa em seu código se você chamar FindResource ou SetResourceReference:

  1. A pesquisa verifica a chave solicitada no dicionário de recursos definido pelo elemento que define a propriedade:

  2. A pesquisa percorre a árvore lógica para cima até o elemento pai e seu dicionário de recursos. Esse processo continua até que o elemento raiz seja alcançado.

  3. Os recursos do aplicativo são verificados. Os recursos do aplicativo são os recursos dentro do dicionário de recursos definidos pelo Application objeto para seu aplicativo WPF.

  4. O dicionário de recursos do tema é verificado para o tema ativo no momento. Se o tema for alterado em runtime, o valor será reavaliado.

  5. Os recursos do sistema são verificados.

O comportamento de exceção (se houver) varia:

  • Se um recurso foi solicitado por uma FindResource chamada e não foi encontrado, uma exceção será lançada.

  • Se um recurso foi solicitado por uma TryFindResource chamada e não foi encontrado, nenhuma exceção será lançada e o valor retornado será null. Se a propriedade que está sendo definida não aceitar null, ainda é possível que uma exceção mais profunda seja lançada, dependendo da propriedade individual que está sendo definida.

  • Se um recurso foi solicitado por uma referência de recurso dinâmico em XAML e não foi encontrado, o comportamento dependerá do sistema de propriedades geral. O comportamento geral é como se nenhuma operação de configuração de propriedade tivesse ocorrido no nível em que o recurso existe. Por exemplo, se você tentar definir o plano de fundo em um elemento de botão individual usando um recurso que não pôde ser avaliado, nenhum valor definido resultará, mas o valor efetivo ainda poderá vir de outros participantes no sistema de propriedades e precedência de valor. Por exemplo, o valor do plano de fundo ainda pode vir de um estilo de botão definido localmente ou do estilo do tema. Para propriedades que não são definidas por estilos de tema, o valor efetivo após uma avaliação de recurso com falha pode vir do valor padrão nos metadados da propriedade.

Restrições

As referências de recursos dinâmicos têm algumas restrições importantes. Pelo menos uma das seguintes condições deve ser verdadeira:

Como a propriedade que está sendo definida deve ser uma DependencyProperty propriedade ou Freezable , a maioria das alterações de propriedade pode se propagar para a interface do usuário porque uma alteração de propriedade (o valor do recurso dinâmico alterado) é reconhecida pelo sistema de propriedades. A maioria dos controles inclui lógica que forçará outro layout de um controle se uma DependencyProperty alteração e essa propriedade puderem afetar o layout. No entanto, nem todas as propriedades que têm uma Extensão de Marcação DynamicResource como seu valor têm a garantia de fornecer atualizações em tempo real na interface do usuário. Essa funcionalidade ainda pode variar dependendo da propriedade e dependendo do tipo que possui a propriedade ou até mesmo da estrutura lógica do seu aplicativo.

Estilos, DataTemplates e chaves implícitas

Embora todos os itens em a ResourceDictionary devam ter uma chave, isso não significa que todos os recursos devam ter um x:Key. Vários tipos de objeto dão suporte a uma chave implícita quando definidos como um recurso, em que o valor da chave é vinculado ao valor de outra propriedade. Esse tipo de chave é conhecido como chave implícita e um x:Key atributo é uma chave explícita. Você pode substituir uma chave implícita especificando uma chave explícita.

Um cenário importante para recursos é quando você define um Style. Na verdade, a é quase sempre definido como uma entrada em um dicionário de recursos, porque os Style estilos são inerentemente destinados à reutilização. Para obter mais informações sobre estilos, consulte Estilos e modelos (WPF .NET).

Os estilos de controles podem ser criados com uma chave implícita e referenciados com ela. Os estilos de tema que definem a aparência padrão de um controle se baseiam nessa chave implícita. Do ponto de vista de solicitá-lo, a chave implícita é a Type do próprio controle. Do ponto de vista da definição dos recursos, a chave implícita é a TargetType do estilo. Dessa forma, se você estiver criando temas para controles personalizados ou criando estilos que interagem com estilos de tema existentes, não será necessário especificar uma diretiva x:Key para isso Style. E se você quiser usar os estilos temáticos, não precisa especificar nenhum estilo. Por exemplo, a seguinte definição de estilo funciona, mesmo que o Style recurso não pareça ter uma chave:

<Style TargetType="Button">
    <Setter Property="Background" Value="#4E1A3D" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="BorderThickness" Value="5" />
    <Setter Property="BorderBrush">
        <Setter.Value>
            <LinearGradientBrush>
                <GradientStop Offset="0.0" Color="#4E1A3D"/>
                <GradientStop Offset="1.0" Color="Salmon"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
</Style>

Esse estilo realmente tem uma chave: a chave implícita: o System.Windows.Controls.Button tipo. Na marcação, você pode especificar a TargetType diretamente como o nome do tipo (ou, opcionalmente, pode usar {x:Type...} para retornar um Type.

Por meio dos mecanismos de estilo de tema padrão usados pelo WPF, esse estilo é aplicado como o estilo de runtime de a Button na página, mesmo que o Button próprio não tente especificar sua Style propriedade ou uma referência de recurso específica para o estilo. Seu estilo definido na página é encontrado mais cedo na sequência de pesquisa do que o estilo do dicionário de temas, usando a mesma chave que o estilo de dicionário de temas tem. Você pode simplesmente especificar <Button>Hello</Button> em qualquer lugar da página, e o estilo definido com TargetType of Button se aplicará a esse botão. Se desejar, você ainda pode codificar explicitamente o estilo com o mesmo valor TargetType de tipo para clareza em sua marcação, mas isso é opcional.

As chaves implícitas para estilos não se aplicam a um controle se OverridesDefaultStyle for true. (Observe também que OverridesDefaultStyle isso pode ser definido como parte do comportamento nativo para a classe de controle, em vez de explicitamente em uma instância do controle.) Além disso, para dar suporte a chaves implícitas para cenários de classe derivada, o controle deve substituir DefaultStyleKey (todos os controles existentes fornecidos como parte do WPF incluem essa substituição). Para obter mais informações sobre estilos, temas e design de controle, consulte Diretrizes para a criação de controles estilizados.

DataTemplate também tem uma chave implícita. A chave implícita para a DataTemplate é o valor da DataType propriedade. DataType também pode ser especificado como o nome do tipo em vez de usar explicitamente {x:Type...}. Para obter detalhes, consulte Visão geral da modelagem de dados.

Confira também