Visão geral sobre criação de controles
The extensibility of the Windows Presentation Foundation (WPF) control model greatly reduces the need to create a new control. However, in certain cases you may still need to create a custom control. This topic discusses the features that minimize your need to create a custom control and the different control authoring models in Windows Presentation Foundation (WPF). This topic also demonstrates how to create a new control.
Este tópico contém as seguintes seções.
- Alternativas para escrever um novo controle.
- Models for Control Authoring
- Control Authoring Basics
- Tópicos relacionados
Alternativas para escrever um novo controle.
Historically, if you wanted to get a customized experience from an existing control, you were limited to changing the standard properties of the control, such as background color, border width, and font size. Se você quis estender a aparência ou comportamento de um controle Além desses parâmetros predefinidos, você precisaria criar um novo controle, geralmente por meio de herança de um controle existente e substituindo o método responsável por desenhar o controle. Embora ainda é uma opção, WPF lhe permite personalizar os controles existentes usando o seu modelo de conteúdo rico, estilos, modelos e disparadores. A lista a seguir fornece exemplos de como esses recursos podem ser usados para criar experiências personalizadas e consistentes sem ter que criar um novo controle.
Rich Content. Muitos dos padrão WPF controles suportam conteúdo rico. Por exemplo, a propriedade content de um Button é do tipo Object, teoricamente nada pode ser exibido em um Button. Para que um botão para exibir uma imagem e texto, você pode adicionar uma imagem e um TextBlock para um StackPanel e atribuir a StackPanel para o Content propriedade. Como os controles podem exibir WPF elementos visuais e dados arbitrários, há menos necessidade de criar um novo controle ou modificar um controle existente para dar suporte a uma visualização complexa. Para obter mais informações sobre o modelo de conteúdo para Button e outros modelos de conteúdo no WPF, consulte Modelo de conteúdo WPF.
Estilos. A Style é uma coleção de valores que representam propriedades para um controle. By using styles, you can create a reusable representation of a desired control appearance and behavior without writing a new control. Por exemplo, suponha que você deseja que todos os seus TextBlock controles tenham a fonte Arial vermelho com um tamanho de fonte de 14. Você pode criar um estilo como um recurso e definir as propriedades adequadas de acordo. Em seguida, cada TextBlock que você adicionar ao seu aplicativo terá a mesma aparência.
Modelos de dados. A DataTemplate permite que você personalize como os dados são exibidos em um controle. Por exemplo, um DataTemplate pode ser usado para especificar como os dados são exibidos em um ListBox. Para obter um exemplo disto, consulte Visão geral sobre Templating de dados. Para além de personalizar a aparência dos dados, um DataTemplate pode incluir elementos de interface do usuário, que oferece uma grande flexibilidade no UIs personalizadas. Por exemplo, usando um DataTemplate, você pode criar um ComboBox em que cada item contém uma caixa de seleção.
Modelos de controle. Muitos controles em WPF usar um ControlTemplate para definir a estrutura e a aparência, o que separa a aparência de um controle da funcionalidade do controle do controle. Você pode mudar drasticamente a aparência de um controle, redefinindo seu ControlTemplate. Por exemplo, suponha que você deseja que um controle semelhante a uma alerta. Esse controle tem uma interface de usuário simples e funcionalidade. O controle é três círculos, somente um dos quais pode acendem por vez. Após alguns reflexo, você talvez perceba que um RadioButton oferece a funcionalidade de apenas um sendo selecionado em uma hora, mas a aparência padrão da RadioButton parece nada com as luzes em um alerta. Porque o RadioButton usa um modelo de controle para definir sua aparência, é fácil de redefinir o ControlTemplate às exigências de controle, e use os botões de opção para tornar o alerta.
Observação
Embora um RadioButton pode usar um DataTemplate, um DataTemplate não é suficiente nesse exemplo.O DataTemplate define a aparência do conteúdo de um controle.No caso de um RadioButton, o conteúdo é tudo o que aparece à direita do círculo que indica se a RadioButton está selecionado.No exemplo do alerta, o botão de opção precisa ser apenas um círculo que pode "acendem." Porque o requisito de aparência para o alerta é tão diferente do que a aparência padrão da RadioButton, é necessário redefinir o ControlTemplate.Em geral um DataTemplate é usado para definir o conteúdo (ou dados) de um controle e um ControlTemplate é usado para definir como um controle está estruturado.
Disparadores. A Trigger permite que você dinamicamente altere a aparência e comportamento de um controle sem criar um novo controle. Por exemplo, suponha que você tem vários ListBox controles em seu aplicativo e deseja que os itens em cada ListBox seja negrito e vermelho quando elas estão selecionadas. Talvez seu primeiro instinto criar uma classe que herda de ListBox e substituir o OnSelectionChanged método para alterar a aparência do item selecionado, mas uma abordagem melhor é adicionar um disparador para um estilo de um ListBoxItem que altera a aparência do item selecionado. Um disparador permite que você alterar os valores de propriedade ou tomar ações com base no valor de uma propriedade. Um EventTrigger permite que você execute ações quando ocorre um evento.
Para obter mais informações sobre estilos, modelos e disparadores, consulte Styling and Templating.
Em geral, se seu controle espelha a funcionalidade de um controle existente, mas você deseja que o controle para uma aparência diferente, você deve primeiro considerar se você pode usar qualquer um dos métodos discutidos nesta seção para alterar a aparência do controle existente.
Models for Control Authoring
O modelo de conteúdo rico, estilos, modelos e disparadores minimizam a necessidade de criar um novo controle. No entanto, se você precisar criar um novo controle, é importante compreender o controle de diferente modelos de autoria WPF. WPF fornece três modelos gerais para criar um controle, cada um deles fornece um conjunto diferente de recursos e o nível de flexibilidade. A base de classes para os três modelos são UserControl, Control, e FrameworkElement.
Deriving from UserControl
A maneira mais simples de criar um controle em WPF deve derivar de UserControl. Quando você cria um controle que herda de UserControl, você adicionar componentes existentes para o UserControl, nomeie os componentes e referência manipuladores de eventos em Extensible Application Markup Language (XAML). Você pode fazer referência os elementos nomeados e definir os manipuladores de eventos no código. Este modelo de desenvolvimento é muito semelhante ao modelo usado para o desenvolvimento de aplicativos em WPF.
If built correctly, a UserControl can take advantage of the benefits of rich content, styles, and triggers. No entanto, se seu controle herda de UserControl, pessoas que usam o controle não será capazes de usar um DataTemplate ou ControlTemplate para personalizar sua aparência. É necessário derivar de Control classe ou uma de suas classes derivadas (diferente de UserControl) para criar um controle personalizado que oferece suporte a modelos.
Benefits of Deriving from UserControl
Considere a derivação de UserControl se todos estes procedimentos se aplicam:
Você deseja criar o seu controle de forma semelhante a como criar um aplicativo.
O controle consiste somente componentes existentes.
You don't need to support complex customization.
Deriving from Control
Deriving from the Control class is the model used by most of the existing WPF controls. Quando você cria um controle que herda do Control classe, você definir sua aparência usando modelos. Fazendo isso, você pode separar a lógica operacional da representação visual. Você também pode assegurar a separação da interface do usuário e lógica, usando os comandos e ligações em vez de eventos e evitando elementos de referência na ControlTemplate sempre que possível. Se a interface do usuário e a lógica do seu controle desacoplados corretamente, um usuário de seu controle poderá redefinir o controle ControlTemplate para personalizar sua aparência. Embora a criação de um personalizado Control não é tão simples quanto a criação de um UserControl, um personalizado Control fornece mais flexibilidade.
Benefits of Deriving from Control
Considere a derivação de Control em vez de usar o UserControl classe se qualquer uma das seguintes aplicar:
Você deseja que a aparência de seu controle para ser personalizável através do ControlTemplate.
Você deseja que o seu controle para oferecer suporte a diferentes temas.
Deriving from FrameworkElement
Controles que derivam de UserControl ou Control dependem redigir elementos existentes. Para muitos cenários, essa é uma solução aceitável, porque qualquer objeto que herda de FrameworkElement pode estar em um ControlTemplate. No entanto, há vezes quando a aparência de um controle requer mais do que a funcionalidade da composição do elemento simples. For these scenarios, basing a component on FrameworkElement is the right choice.
Há dois métodos padrão para construção FrameworkElement-componentes baseados em: processamento direto e composição de elemento personalizado. Direct rendering involves overriding the OnRender method of FrameworkElement and providing DrawingContext operations that explicitly define the component visuals. This is the method used by Image and Border. Composição do elemento personalizado envolve o uso de objetos do tipo Visual para compor a aparência do componente. For an example, see Using DrawingVisual Objects. Tracké um exemplo de um controle em WPF que usa o elemento personalizado composição. Também é possível misturar processamento direto e composição de elemento personalizado no mesmo controle.
Benefits of Deriving from FrameworkElement
Considere a derivação de FrameworkElement se qualquer uma das seguintes aplicar:
Você deseja ter controle preciso sobre a aparência de seu controle além do que é fornecido pela composição de elemento simples.
You want to define the appearance of your control by defining your own render logic.
You want to compose existing elements in novel ways that go beyond what is possible with UserControl and Control.
Control Authoring Basics
Conforme discutido anteriormente, um dos mais poderosos recursos de WPF é a capacidade de ir além de definir as propriedades básicas de um controle para alterar sua aparência e comportamento, mas ainda não precisar criar um controle personalizado. O estilo, a vinculação de dados e recursos de disparador são possibilitados pela WPF o sistema de propriedade e o WPF sistema de eventos. As seções a seguir descrevem algumas práticas recomendadas que você deve seguir, independentemente do modelo utilizado para criar um controle personalizado, para que os usuários do seu controle personalizado podem usar esses recursos, como fariam com um controle que está incluído no WPF.
Use Dependency Properties
Quando uma propriedade é uma propriedade de dependência, é possível fazer o seguinte:
Defina a propriedade em um estilo.
Vincule a propriedade a uma fonte de dados.
Use um recurso dinâmico como o valor da propriedade.
Anime a propriedade.
Se desejar que uma propriedade do seu controle para suportar qualquer essa funcionalidade, você deve implementá-la como uma propriedade de dependência. O exemplo a seguir define uma propriedade de dependência denominada Value , fazendo o seguinte:
Definir um DependencyProperty identificador chamado ValueProperty como um public static readonly campo.
Registra o nome da propriedade com o sistema de propriedade, chamando DependencyProperty.Register, para especificar o seguinte:
O nome da propriedade.
O tipo da propriedade.
O tipo que possui a propriedade.
Os metadados da propriedade. Os metadados contêm o valor da propriedade padrão, um CoerceValueCallback e um PropertyChangedCallback.
Definir um CLR propriedade de wrapper denominada Value, que é o mesmo nome que é usado para registrar a propriedade de dependência, Implementando a propriedade get e set acessadores. Observe que o get e set acessadores só chamar GetValue e SetValue respectivamente. É recomendável que os acessadores de propriedades de dependência não contêm lógica adicional porque os clientes e WPF podem ignorar os assessores e chamada GetValue e SetValue diretamente. Por exemplo, quando uma propriedade é vinculada aos dados de um fonte, a propriedade set acessador não é chamado. Em vez de adicionar lógica adicional para get e set acessadores, use o ValidateValueCallback, CoerceValueCallback, e PropertyChangedCallback delegados para responder ou verificar o valor quando ele alterações. Para obter mais informações sobre esses retornos de chamada, consulte Validação e Callbacks de Propriedade de Dependência.
Definir um método para o CoerceValueCallback chamado CoerceValue. CoerceValuegarante que Value é maior ou igual a MinValue e menor ou igual a MaxValue.
Definir um método para o PropertyChangedCallback, denominada OnValueChanged. OnValueChangedcria um RoutedPropertyChangedEventArgs<T> de objeto e se prepara para aumentar a ValueChanged roteados eventos. Eventos roteados são discutidos na próxima seção.
''' <summary>
''' Identifies the Value dependency property.
''' </summary>
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Decimal), GetType(NumericUpDown), New FrameworkPropertyMetadata(MinValue, New PropertyChangedCallback(AddressOf OnValueChanged), New CoerceValueCallback(AddressOf CoerceValue)))
''' <summary>
''' Gets or sets the value assigned to the control.
''' </summary>
Public Property Value() As Decimal
Get
Return CDec(GetValue(ValueProperty))
End Get
Set(ByVal value As Decimal)
SetValue(ValueProperty, value)
End Set
End Property
Private Shared Overloads Function CoerceValue(ByVal element As DependencyObject, ByVal value As Object) As Object
Dim newValue As Decimal = CDec(value)
Dim control As NumericUpDown = CType(element, NumericUpDown)
newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue))
Return newValue
End Function
Private Shared Sub OnValueChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
Dim control As NumericUpDown = CType(obj, NumericUpDown)
Dim e As New RoutedPropertyChangedEventArgs(Of Decimal)(CDec(args.OldValue), CDec(args.NewValue), ValueChangedEvent)
control.OnValueChanged(e)
End Sub
/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value", typeof(decimal), typeof(NumericUpDown),
new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),
new CoerceValueCallback(CoerceValue)));
/// <summary>
/// Gets or sets the value assigned to the control.
/// </summary>
public decimal Value
{
get { return (decimal)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static object CoerceValue(DependencyObject element, object value)
{
decimal newValue = (decimal)value;
NumericUpDown control = (NumericUpDown)element;
newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));
return newValue;
}
private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
NumericUpDown control = (NumericUpDown)obj;
RoutedPropertyChangedEventArgs<decimal> e = new RoutedPropertyChangedEventArgs<decimal>(
(decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
control.OnValueChanged(e);
}
For more information, see Propriedades de Dependência Personalizada.
Use roteadas eventos
Assim como Propriedades de dependência estende a noção de CLR Propriedades com funcionalidade adicional, roteados eventos estende a noção do padrão CLR eventos. Quando você cria um novo WPF o controle, também é uma boa prática para implementar o seu evento como um evento roteado, porque um evento roteado suporta o seguinte comportamento:
Eventos podem ser tratados em um pai de vários controles. Se um evento é um evento de bubbling, um único pai na árvore de elemento pode assinar o evento. Em seguida, os autores do aplicativo podem usar um manipulador para responder ao evento de vários controles. Por exemplo, se o controle for parte de cada item em um ListBox (porque ela está incluída em um DataTemplate), o desenvolvedor do aplicativo pode definir o manipulador de eventos para o evento do controle na ListBox. Sempre que o evento ocorre em qualquer um dos controles, o manipulador de evento é chamado.
Eventos roteados que podem ser usados em um EventSetter, que permite que os desenvolvedores de aplicativos especificar o manipulador de um evento em um estilo.
Eventos roteados que podem ser usados em um EventTrigger, que é útil para propriedades de animação usando XAML. For more information, see Revisão de Animação.
O exemplo a seguir define um evento roteado, fazendo o seguinte:
Definir um RoutedEvent identificador chamado ValueChangedEvent como um public static readonly campo.
Registra o evento roteado chamando o EventManager.RegisterRoutedEvent método. O exemplo especifica as informações a seguir, quando ele chama RegisterRoutedEvent:
O nome do evento é ValueChanged.
A estratégia de roteamento é Bubble, o que significa que um manipulador de eventos na origem (o objeto que gera o evento) é chamado primeiro e, em seguida, os manipuladores de eventos nos elementos de pai da fonte são chamados em sucessão, começando com o manipulador de eventos no elemento mais próximo do pai.
O tipo de manipulador de eventos é RoutedPropertyChangedEventHandler<T>, construído com uma Decimal tipo.
O tipo de proprietário do evento é NumericUpDown.
Declarar um evento público chamado ValueChanged e inclui declarações de acessador do evento. As chamadas de exemplo AddHandler na add declaração de assessor e RemoveHandler na remove declaração de assessor para usar o WPF Serviços de eventos.
Criar um método protegido e virtual chamado OnValueChanged que eleva o ValueChanged de evento.
''' <summary>
''' Identifies the ValueChanged routed event.
''' </summary>
Public Shared ReadOnly ValueChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, GetType(RoutedPropertyChangedEventHandler(Of Decimal)), GetType(NumericUpDown))
''' <summary>
''' Occurs when the Value property changes.
''' </summary>
Public Custom Event ValueChanged As RoutedPropertyChangedEventHandler(Of Decimal)
AddHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
MyBase.AddHandler(ValueChangedEvent, value)
End AddHandler
RemoveHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
MyBase.RemoveHandler(ValueChangedEvent, value)
End RemoveHandler
RaiseEvent(ByVal sender As System.Object, ByVal e As RoutedPropertyChangedEventArgs(Of Decimal))
End RaiseEvent
End Event
''' <summary>
''' Raises the ValueChanged event.
''' </summary>
''' <param name="args">Arguments associated with the ValueChanged event.</param>
Protected Overridable Sub OnValueChanged(ByVal args As RoutedPropertyChangedEventArgs(Of Decimal))
MyBase.RaiseEvent(args)
End Sub
/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
"ValueChanged", RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumericUpDown));
/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
{
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }
}
/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args)
{
RaiseEvent(args);
}
For more information, see Visão geral sobre eventos roteados and Como: Criar um evento roteado personalizado.
Usar ligação
Separar a interface do usuário do seu controle de sua lógica, considere o uso de vinculação de dados. Isso é particularmente importante se você definir a aparência de seu controle, usando um ControlTemplate. Quando você usa a ligação de dados, você poderá eliminar a necessidade de fazer referência a partes específicas da interface do usuário do código. É uma boa idéia para evitar a referência a elementos que estão na ControlTemplate porque quando o código faz referência a elementos que estão na ControlTemplate e o ControlTemplate é alterado, as necessidades do elemento referenciado a serem incluídos nos novos ControlTemplate.
As seguintes atualizações de exemplo do TextBlock da NumericUpDown controle, atribuindo um nome para ele e fazer referência a caixa de texto por nome no código.
<Border BorderThickness="1" BorderBrush="Gray" Margin="2"
Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
Private Sub UpdateTextBlock()
valueText.Text = Value.ToString()
End Sub
private void UpdateTextBlock()
{
valueText.Text = Value.ToString();
}
O exemplo a seguir usa a ligação para realizar a mesma coisa.
<Border BorderThickness="1" BorderBrush="Gray" Margin="2"
Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<!--Bind the TextBlock to the Value property-->
<TextBlock
Width="60" TextAlignment="Right" Padding="5"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:NumericUpDown}},
Path=Value}"/>
</Border>
For more information about data binding, see Revisão de Associação de Dados.
Design para Designers
Para receber suporte para controles personalizados do WPF na WPF Designer for Visual Studio (por exemplo, com a janela Propriedades de edição de propriedade), siga estas diretrizes. Para obter mais informações sobre o desenvolvimento para o WPF Designer, consulte WPF Designer.
Propriedades de dependência
Certifique-se de implementar CLR get e set acessadores conforme descrito anteriormente, "usar dependência propriedades". Os designers podem usar o wrapper para detectar a presença de uma propriedade de dependência, mas, como WPF e clientes do controle, não é necessária para chamar os acessadores ao obter ou definir a propriedade.
Attached Properties
You should implement attached properties on custom controls using the following guidelines:
Ter um public static readonly DependencyProperty do formulário PropertyNameProperty que foi criado usando o RegisterAttached método. The property name that is passed to RegisterAttached must match PropertyName.
Implement a pair of public static CLR methods named SetPropertyName and GetPropertyName. Both methods should accept a class derived from DependencyProperty as their first argument. The SetPropertyName method also accepts an argument whose type matches the registered data type for the property. The GetPropertyName method should return a value of the same type. Se o SetPropertyName método estiver ausente, a propriedade é somente leitura.
SetPropertyName and GetPropertyName must route directly to the GetValue and SetValue methods on the target dependency object, respectively. Os designers podem acessar a propriedade anexada chamando por meio do invólucro do método ou fazendo uma chamada direta para o objeto de dependência de destino.
Para obter mais informações sobre propriedades anexadas, consulte Attached Properties Overview.
Definir e usar os recursos compartilhados
Você pode incluir o controle no mesmo assembly como seu aplicativo, ou você pode empacotar seu controle em um assembly separado que pode ser usado em vários aplicativos. Na maior parte, as informações discutidas neste tópico se aplica, independentemente do método que você usar. No entanto, há uma diferença digna. Quando você coloca um controle no mesmo assembly como um aplicativo, você é livre para adicionar recursos globais para o arquivo ser. Mas um assembly que contém somente os controles não tem um Application objeto associado a ele, portanto, um arquivo ser não está disponível.
Quando um aplicativo procura-se um recurso, ele se parece em três níveis na seguinte ordem:
O nível de elemento.
O sistema inicia com o elemento que faz referência o recurso e procura os recursos do pai lógico e assim por diante até que o elemento raiz seja alcançado.
O nível de aplicativo.
Recursos definidos pelo Application objeto.
O nível de tema.
Dicionários de tema são armazenados em uma subpasta denominada temas. Os arquivos na pasta temas correspondem a temas. Por exemplo, você pode ter Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml e assim por diante. Você também pode ter um arquivo chamado Generic XAML. Quando o sistema procura um recurso no nível de temas, ele primeiro procura por ele no arquivo de tema específico e, em seguida, procura-lo em Generic.
Quando o controle está em um assembly separado do aplicativo, você deve colocar seus recursos globais no nível de elemento ou no nível do tema. Os dois métodos têm suas vantagens.
Definir recursos no nível de elemento
Você pode definir os recursos compartilhados no nível de elemento, criando um dicionário de recurso personalizado e mesclá-la com o dicionário de recurso do controle. Quando você usa esse método, você pode nomear o arquivo de recurso que quiser e pode ser na mesma pasta como seus controles. Recursos no nível do elemento também podem usar as cadeias de caracteres simples como chaves. O exemplo a seguir cria um LinearGradientBrush arquivo de recurso denominado Dictionary1.xaml.
<ResourceDictionary
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">
<LinearGradientBrush
x:Key="myBrush"
StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
</LinearGradientBrush>
</ResourceDictionary>
Depois de ter definido seu dicionário, você precisará mesclar com o dicionário de recurso do controle. Você pode fazer isso usando XAML ou código.
O exemplo a seguir mescla um dicionário de recurso usando XAML.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
A desvantagem dessa abordagem é que um ResourceDictionary objeto é criado sempre que a referência do proprietário. Por exemplo, se você tiver 10 controles personalizados na sua biblioteca e mesclar os dicionários de recurso compartilhado para cada controle usando XAML, você criar 10 idênticos ResourceDictionary objetos. Você pode evitar isso, criando uma classe estática que mescla os recursos no código e retorna o resultante ResourceDictionary.
O exemplo a seguir cria uma classe que retorna um compartilhado ResourceDictionary.
internal static class SharedDictionaryManager
{
internal static ResourceDictionary SharedDictionary
{
get
{
if (_sharedDictionary == null)
{
System.Uri resourceLocater =
new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml",
System.UriKind.Relative);
_sharedDictionary =
(ResourceDictionary)Application.LoadComponent(resourceLocater);
}
return _sharedDictionary;
}
}
private static ResourceDictionary _sharedDictionary;
}
O exemplo a seguir mescla o recurso compartilhado com os recursos de um controle personalizado no construtor do controle antes de chamar InitilizeComponent. Porque o SharedDictionaryManager.SharedDictionary é uma propriedade estática, o [ResourceDictionary](https://msdn.microsoft.com/pt-br/library/ms589726\(v=vs.100\)) é criado somente uma vez. Porque o dicionário de recurso foi mesclado antes de InitializeComponent foi chamado, os recursos estão disponíveis para o controle no seu XAML arquivo.
```ElementResourcesCustomControlLibrary
public NumericUpDown()
{
this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
InitializeComponent();
}
#### Definir recursos no nível do tema
WPFpermite que você criar recursos para diferentes temas do Windows. Como o autor de um controle, você pode definir um recurso para um tema específico alterar a aparência do controle dependendo do tema está em uso. Por exemplo, a aparência de um [Button](https://msdn.microsoft.com/pt-br/library/ms609089\(v=vs.100\)) Windows Classic tema (o tema padrão para o Windows 2000) difere de um [Button](https://msdn.microsoft.com/pt-br/library/ms609089\(v=vs.100\)) no tema Luna do Windows (o tema padrão para o Windows XP) porque o [Button](https://msdn.microsoft.com/pt-br/library/ms609089\(v=vs.100\)) usa um diferente [ControlTemplate](https://msdn.microsoft.com/pt-br/library/ms609827\(v=vs.100\)) para cada tema.
Os recursos que são específicos para um tema são mantidos em um dicionário de recurso com um nome de arquivo específico. Esses arquivos devem estar em uma pasta chamada temas ou seja, uma subpasta da pasta que contém o controle. A tabela a seguir lista os arquivos de dicionário de recurso e o tema que está associado a cada arquivo:
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th><p>Nome do arquivo de dicionário de recurso</p></th>
<th><p>Tema do Windows</p></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><p>Classic.XAML</p></td>
<td><p>Tema clássico do Windows 9 x / 2000 procura no Windows XP</p></td>
</tr>
<tr class="even">
<td><p>Luna.NormalColor.XAML</p></td>
<td><p>Tema padrão azul no Windows XP</p></td>
</tr>
<tr class="odd">
<td><p>Luna.Homestead.XAML</p></td>
<td><p>Tema de oliva no Windows XP</p></td>
</tr>
<tr class="even">
<td><p>Luna.metallic.XAML</p></td>
<td><p>Tema prateado no Windows XP</p></td>
</tr>
<tr class="odd">
<td><p>Royale.NormalColor.XAML</p></td>
<td><p>Tema padrão no Windows XP Media Center Edition</p></td>
</tr>
<tr class="even">
<td><p>Aero.NormalColor.XAML</p></td>
<td><p>Tema padrão no Windows Vista</p></td>
</tr>
</tbody>
</table>
Você não precisará definir um recurso para cada tema. Se um recurso não está definido para um tema específico, o controle verificará Classic.xaml para o recurso. Se o recurso não está definido no arquivo que corresponda ao tema atual ou em Classic.xaml, o controle usa o recurso genérico, o que está em um arquivo de dicionário de recurso chamado Generic. O Generic arquivo está localizado na mesma pasta que os arquivos de dicionário de recurso específico do tema. Embora Generic não corresponde a um tema específico do Windows, ainda é um dicionário de nível de tema.
[Controle de NumericUpDown personalizado com tema e exemplo de suporte de automação da interface do usuário](https://go.microsoft.com/fwlink/?linkid=160025) contém dois dicionários de recurso para o NumericUpDown controle: uma é em Generic e uma está em Luna.NormalColor.xaml. Você pode executar o aplicativo e alternar entre o tema prateado no Windows XP e outro tema para ver a diferença entre os modelos de controle de dois. (Se você estiver executando o Windows Vista, você pode renomear Luna.NormalColor.xaml para Aero.NormalColor.xaml e alternar entre dois temas, como o tema clássico do Windows e o tema padrão para o Windows Vista.)
Quando você colocar um [ControlTemplate](https://msdn.microsoft.com/pt-br/library/ms609827\(v=vs.100\)) em qualquer um dos arquivos de dicionário de recurso específico do tema, você deve criar um construtor estático para o controle e a chamada a [OverrideMetadata(Type, PropertyMetadata)](https://msdn.microsoft.com/pt-br/library/ms597491\(v=vs.100\)) método o [DefaultStyleKey](https://msdn.microsoft.com/pt-br/library/ms600876\(v=vs.100\)), conforme mostrado no exemplo a seguir.
``` vb
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Definição e chaves de referência para recursos do tema
Quando você define um recurso no nível de elemento, você pode atribuir uma seqüência de caracteres como sua chave e acessar o recurso através da seqüência de caracteres. Quando você define um recurso no nível do tema, você deve usar um ComponentResourceKey como a tecla. O exemplo a seguir define um recurso em Generic.
<LinearGradientBrush
x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Painter},
ResourceId=MyEllipseBrush}"
StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Blue" Offset="0" />
<GradientStop Color="Red" Offset="0.5" />
<GradientStop Color="Green" Offset="1"/>
</LinearGradientBrush>
O exemplo a seguir faz referência a recurso especificando a ComponentResourceKey como a tecla.
<RepeatButton
Grid.Column="1" Grid.Row="0"
Background="{StaticResource {ComponentResourceKey
TypeInTargetAssembly={x:Type local:NumericUpDown},
ResourceId=ButtonBrush}}">
Up
</RepeatButton>
<RepeatButton
Grid.Column="1" Grid.Row="1"
Background="{StaticResource {ComponentResourceKey
TypeInTargetAssembly={x:Type local:NumericUpDown},
ResourceId=ButtonBrush}}">
Down
</RepeatButton>
Especificar o local dos recursos do tema
Para localizar os recursos para um controle, o aplicativo de hospedagem precisa saber que o assembly contém recursos específicos do controle. Você pode realizar que adicionando a ThemeInfoAttribute para o assembly que contém o controle. O ThemeInfoAttribute tem um GenericDictionaryLocation propriedade que especifica o local dos recursos genéricos, e um ThemeDictionaryLocation propriedade que especifica o local dos recursos específicos do tema.
O exemplo a seguir define o GenericDictionaryLocation e ThemeDictionaryLocation Propriedades para SourceAssembly, para especificar que os recursos genéricos e específicos do tema estão no mesmo assembly como o controle.
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
ResourceDictionaryLocation.SourceAssembly)]