Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
A extensibilidade do modelo de controle do Windows Presentation Foundation (WPF) reduz consideravelmente a necessidade de criar um novo controle. No entanto, em certos casos, você ainda pode precisar criar um controle personalizado. Este tópico discute os recursos que minimizam a necessidade de criar um controle personalizado e os diferentes modelos de criação de controle no Windows Presentation Foundation (WPF). Este tópico também demonstra como criar um novo controle.
Alternativas para escrever um novo controle
Historicamente, se você quisesse obter uma experiência personalizada de um controle existente, estava limitado a alterar as propriedades padrão do controle, como cor do plano de fundo, largura da borda e tamanho da fonte. Se você desejasse estender a aparência ou o comportamento de um controle além desses parâmetros predefinidos, você precisaria criar um novo controle, geralmente herdando de um controle existente e substituindo o método responsável por desenhar o controle. Embora isso ainda seja uma opção, o WPF permite que você personalize os controles existentes usando seu modelo de conteúdo avançado, estilos, modelos e gatilhos. 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.
Conteúdo rico. Muitos dos controles WPF padrão suportam conteúdo avançado. Por exemplo, a propriedade content de um Button é do tipo Object, portanto, teoricamente, qualquer coisa pode ser exibida em um Button. Para que um botão exiba uma imagem e texto, pode-se adicionar uma imagem e um TextBlock a um StackPanel e atribuir o StackPanel à propriedade Content. Como os controles podem exibir elementos visuais WPF 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 do WPF.
Estilos. A Style é uma coleção de valores que representam propriedades para um controle. Usando estilos, você pode criar uma representação reutilizável de uma aparência e comportamento de controle desejados sem escrever um novo controle. Por exemplo, suponha que você queira que todos os seus TextBlock controles tenham fonte Arial vermelha com um tamanho de fonte de 14. Você pode criar um estilo como um recurso e definir as propriedades apropriadas de acordo. Em seguida, cada TextBlock que adicionar à sua aplicação terá a mesma aparência.
Modelos de dados. A DataTemplate permite personalizar como os dados são exibidos em um controle. Por exemplo, um DataTemplate pode ser usado para especificar como os dados são exibidos numa ListBoxinterface. Para obter um exemplo disso, consulte Visão geral de modelagem de dados. Além de personalizar a aparência dos dados, um DataTemplate pode incluir elementos de interface do utilizador, o que lhe confere muita flexibilidade em UIs personalizadas. Por exemplo, ao usar um DataTemplate, pode-se criar um ComboBox no qual cada item contém uma caixa de seleção.
Modelos de controle. Muitos controles no WPF usam a ControlTemplate para definir a estrutura e aparência do controle, que separa a aparência de um controle da funcionalidade do controle. Você pode alterar drasticamente a aparência de um controlo redefinindo o seu ControlTemplate. Por exemplo, suponha que você queira um controle que se pareça com um semáforo. Este controle tem uma interface de usuário simples e funcionalidade. O controle é de três círculos, dos quais apenas um pode ser iluminado de cada vez. Após alguma reflexão, poderás perceber que um RadioButton oferece a funcionalidade de apenas um ser selecionado de cada vez, mas a aparência padrão de RadioButton não se assemelha em nada às luzes de um semáforo. Como o RadioButton usa um template de controlo para definir a sua aparência, é fácil redefinir o ControlTemplate para se ajustar aos requisitos do controlo e usar botões de opção para criar o seu semáforo.
Observação
Embora a RadioButton possa usar um DataTemplate, a DataTemplate não é suficiente neste exemplo. O DataTemplate define a aparência do conteúdo de um controle. No caso de um RadioButton, o conteúdo é o que aparece à direita do círculo que indica se o RadioButton está selecionado. No exemplo do semáforo, o botão rádio deve ser apenas um círculo que consiga "acender". Como o requisito de aparência para o semáforo é tão diferente da aparência padrão do RadioButton, é necessário redefinir o ControlTemplate. Em geral, a DataTemplate é usado para definir o conteúdo (ou dados) de um controle, e a ControlTemplate é usado para definir como um controle é estruturado.
Gatilhos. A Trigger permite que você altere dinamicamente a aparência e o comportamento de um controle sem criar um novo controle. Por exemplo, suponha que você tenha vários ListBox controles em seu aplicativo e queira que os itens em cada um ListBox sejam negrito e vermelho quando forem selecionados. Seu primeiro instinto pode ser criar uma classe que herda de ListBox e substitui o método OnSelectionChanged para alterar a aparência do item selecionado, mas uma abordagem melhor é adicionar um gatilho a um estilo de ListBoxItem que altera a aparência do item selecionado. Um gatilho permite alterar valores de propriedade ou executar 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 gatilhos, consulte Estilo e modelos.
Em geral, se o controle espelha a funcionalidade de um controle existente, mas você deseja que o controle pareça 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.
Modelos para autoria de controle
O modelo de conteúdo avançado, estilos, modelos e gatilhos minimizam a necessidade de criar um novo controle. No entanto, se você precisar criar um novo controle, é importante entender os diferentes modelos de criação de controle no WPF. O WPF fornece três modelos gerais para criar um controle, cada um dos quais fornece um conjunto diferente de recursos e nível de flexibilidade. As classes base para os três modelos são UserControl, Controle FrameworkElement.
Derivando de UserControl
A maneira mais simples de criar um controle no WPF é derivar de UserControl. Ao criar um controle herdado do UserControl, você adiciona componentes existentes ao UserControl, nomeia os componentes e referencia os manipuladores de eventos em XAML. Em seguida, você pode fazer referência aos 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 no WPF.
Se for construído corretamente, um UserControl pode aproveitar os benefícios de conteúdo avançado, estilos avançados e gatilhos avançados. No entanto, se o seu controlo herdar do UserControl, as pessoas que o utilizam não poderão utilizar um DataTemplate ou ControlTemplate para personalizar a sua aparência. É necessário derivar da Control classe ou de uma de suas classes derivadas (diferente de UserControl) para criar um controle personalizado que ofereça suporte a modelos.
Benefícios de derivar de UserControl
Considere derivar de UserControl se todos os itens a seguir se aplicarem:
Você deseja criar seu controle de forma semelhante a como você cria um aplicativo.
Seu controle consiste apenas em componentes existentes.
Você não precisa oferecer suporte a personalizações complexas.
Derivando do controle
Derivando da Control classe é o modelo usado pela maioria dos controles WPF existentes. Quando você cria um controle que herda da Control classe, você define sua aparência usando modelos. Ao fazer isso, você separa a lógica operacional da representação visual. Você também pode garantir o desacoplamento da interface do usuário e da lógica usando comandos e associações em vez de eventos e evitando elementos de referência no ControlTemplate sempre que possível. Se a interface do utilizador e a lógica do seu controlo estiverem devidamente dissociadas, um utilizador do seu controlo poderá redefinir o componente ControlTemplate para personalizar a sua aparência. Embora construir um personalizado Control não seja tão simples quanto construir um UserControl, um personalizado Control oferece mais flexibilidade.
Benefícios de derivar do controle
Considere derivar de Control em vez de usar a UserControl classe se qualquer uma das seguintes situações se aplicar:
Você deseja que a aparência do seu controle seja personalizável através do ControlTemplate.
Você quer que seu controle suporte diferentes temas.
Derivando de FrameworkElement
Controles que derivam de UserControl ou Control dependem da composição de elementos existentes. Para muitos cenários, esta é uma solução aceitável, porque qualquer objeto que herde de FrameworkElement pode estar num ControlTemplate. No entanto, há momentos em que a aparência de um controle requer mais do que a funcionalidade de composição simples de elementos. Para esses cenários, basear um componente em FrameworkElement é a escolha certa.
Existem dois métodos padrão para a construção de componentes baseados em FrameworkElement: a renderização direta e a composição de elementos personalizados. A renderização direta envolve anular o método OnRender de FrameworkElement e fornecer operações DrawingContext que definem explicitamente os visuais do componente. Este é o método utilizado por Image e Border. A composição de elementos personalizados envolve o uso de objetos do tipo Visual para compor a aparência do componente. Para obter um exemplo, consulte Usando objetos DrawingVisual. Track é um exemplo de um controle no WPF que usa a composição de elementos personalizados. Também é possível misturar renderização direta e composição de elementos personalizados no mesmo controle.
Benefícios de derivar do FrameworkElement
Considere derivar de FrameworkElement se alguma das seguintes situações se aplicarem:
Você quer ter um controlo preciso sobre a aparência do seu controlo para além do que é fornecido pela simples composição de elementos.
Você deseja definir a aparência do seu controle definindo sua própria lógica de renderização.
Você quer compor elementos existentes de maneiras novas que vão além do que é possível com UserControl e Control.
Noções básicas de criação de controle
Como discutido anteriormente, um dos recursos mais poderosos do WPF é a capacidade de ir além da definição de propriedades básicas de um controle para alterar sua aparência e comportamento, mas ainda não precisa criar um controle personalizado. Os recursos de estilo, vinculação de dados e gatilho são possibilitados pelo sistema de propriedades WPF e pelo sistema de eventos WPF. As seções a seguir descrevem algumas práticas que você deve seguir, independentemente do modelo usado para criar o controle personalizado, para que os usuários do seu controle personalizado possam usar esses recursos da mesma forma que fariam para um controle incluído no WPF.
Usar Propriedades de Dependência
Quando uma propriedade é uma propriedade de dependência, é possível fazer o seguinte:
Defina a propriedade num estilo.
Associe a propriedade a uma fonte de dados.
Use um recurso dinâmico como o valor da propriedade.
Anime a propriedade.
Se você quiser que uma propriedade do seu controle ofereça suporte a qualquer uma dessas funcionalidades, você deve implementá-la como uma propriedade de dependência. O exemplo a seguir define uma propriedade de dependência nomeada Value fazendo o seguinte:
Defina um DependencyProperty identificador nomeado
ValuePropertycomo umpublicstaticreadonlycampo.Registre o nome da propriedade no sistema de propriedades, chamando DependencyProperty.Register, para especificar o seguinte:
O nome da propriedade.
O tipo de propriedade.
O tipo que possui o imóvel.
Os metadados da propriedade. Os metadados contêm o valor padrão da propriedade, a CoerceValueCallback e a PropertyChangedCallback.
Defina uma propriedade de wrapper CLR chamada
Value, que é o mesmo nome usado para registrar a propriedade de dependência, implementando a propriedadegetesetos acessadores. Observe que os acessadoresgetesetapenas chamam GetValue e SetValue respetivamente. É recomendável que os acessadores de propriedades de dependência não contenham lógica adicional porque os clientes e o WPF podem ignorar os acessadores e chamar GetValue e SetValue diretamente. Por exemplo, quando uma propriedade é vinculada a uma fonte de dados, o acessador dasetpropriedade não é chamado. Em vez de adicionar lógica adicional aos acessadores get e set, use os delegados ValidateValueCallback, CoerceValueCallback e PropertyChangedCallback para responder ou verificar o valor quando ele for alterado. Para obter mais informações sobre esses retornos de chamada, consulte Retornos de chamada e validação de propriedade de dependência.Defina um método para o CoerceValueCallback chamado
CoerceValue.CoerceValuegarante queValueé maior ou igual aMinValuee menor ou igual aMaxValue.Defina um método para o PropertyChangedCallback, chamado
OnValueChanged.OnValueChangedCria um objeto RoutedPropertyChangedEventArgs<T> e prepara-se para gerar o evento encaminhadoValueChanged. Os eventos roteados são discutidos na próxima seção.
/// <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);
}
''' <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
Para obter mais informações, consulte Propriedades de dependência personalizadas.
Usar eventos roteados
Assim como as propriedades de dependência estendem a noção de propriedades CLR com funcionalidade adicional, os eventos roteados estendem a noção de eventos CLR padrão. Quando você cria um novo controle WPF, também é uma boa prática implementar seu evento como um evento roteado porque um evento roteado oferece suporte ao seguinte comportamento:
Os eventos podem ser manipulados em um pai de vários controles. Se um evento for um evento borbulhante, um único pai na árvore de elementos poderá se inscrever no evento. Em seguida, os autores de aplicativos podem usar um manipulador para responder ao evento de vários controles. Por exemplo, se o seu controlo for uma parte de cada item em um ListBox (porque ele está incluído em um DataTemplate), o desenvolvedor do aplicativo pode definir o manipulador de eventos para o evento do seu controlo no ListBox. Sempre que o evento ocorre em qualquer um dos controles, o manipulador de eventos é chamado.
Os eventos roteados podem ser usados em um EventSetter, que permite que os desenvolvedores de aplicativos especifiquem o manipulador de um evento dentro de um estilo.
Os eventos roteados podem ser usados em um EventTrigger, que é útil para animar propriedades usando XAML. Para obter mais informações, consulte Visão geral da animação .
O exemplo a seguir define um evento roteado fazendo o seguinte:
Defina um RoutedEvent identificador nomeado
ValueChangedEventcomo umpublicstaticreadonlycampo.Registre o evento roteado chamando o EventManager.RegisterRoutedEvent método. O exemplo especifica as seguintes informações quando 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 pai da origem são chamados sucessivamente, começando com o manipulador de eventos no elemento pai mais próximo.
O tipo do manipulador de eventos é RoutedPropertyChangedEventHandler<T>, construído com o tipo Decimal.
O tipo proprietário do evento é
NumericUpDown.
Declare um evento público chamado
ValueChangede incluir os métodos de acesso ao evento. O exemplo invoca AddHandler na declaração do acessadoradde RemoveHandler na declaração do acessadorremovepara utilizar os serviços de eventos do WPF.Crie um método virtual protegido chamado
OnValueChangedque gera oValueChangedevento.
/// <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);
}
''' <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
Para obter mais informações, consulte Visão geral de eventos roteados e Criar um evento roteado personalizado.
Usar vinculação
Para dissociar a interface do usuário do seu controle de sua lógica, considere o uso da associação de dados. Isso é particularmente importante se você definir a aparência do seu controle usando um ControlTemplatearquivo . Ao usar a associação de dados, você poderá eliminar a necessidade de fazer referência a partes específicas da interface do usuário a partir do código. É uma boa ideia evitar fazer referência a elementos que estão no ControlTemplate porque quando o código faz referência a elementos que estão no ControlTemplate e o ControlTemplate é alterado, o elemento referenciado precisa ser incluído no novo ControlTemplate.
O exemplo a seguir atualiza o TextBlock do controlo NumericUpDown, atribuindo um nome a ele e fazendo referência à caixa de texto pelo 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 void UpdateTextBlock()
{
valueText.Text = Value.ToString();
}
Private Sub UpdateTextBlock()
valueText.Text = Value.ToString()
End Sub
O exemplo a seguir usa a vinculaçã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>
Para obter mais informações sobre vinculação de dados, consulte Visão geral da vinculação de dados .
Design para Designers
Para receber suporte para controles WPF personalizados no WPF Designer para Visual Studio (por exemplo, edição de propriedade com a janela Propriedades), siga estas diretrizes. Para obter mais informações sobre como desenvolver para o WPF Designer, consulte Design XAML no Visual Studio.
Propriedades de dependência
Certifique-se de implementar CLR get e set acessadores conforme descrito anteriormente, em "Usar propriedades de dependência". Os designers podem usar o wrapper para detetar a presença de uma propriedade de dependência, mas eles, como o WPF e os clientes do controle, não são obrigados a chamar os acessadores ao obter ou definir a propriedade.
Propriedades anexadas
Você deve implementar propriedades anexadas em controles personalizados usando as seguintes diretrizes:
Tenha um
publicstaticreadonlyDependencyProperty do formulário PropertyNamePropertyque foi criado usando o método RegisterAttached. O nome da propriedade que é passado para deve corresponder a RegisterAttachedPropertyName.Implemente um par de
publicstaticmétodos CLR chamadosSetPropertyName eGetPropertyName. Ambos os métodos devem aceitar uma classe derivada de DependencyProperty como seu primeiro argumento. OSetmétodo PropertyName também aceita um argumento cujo tipo corresponde ao tipo de dados registrado para a propriedade. OGetmétodo PropertyName deve retornar um valor do mesmo tipo. Se oSetmétodo PropertyName estiver ausente, a propriedade será marcada como somente leitura.SetPropertyName eGetPropertyName devem rotear diretamente para os GetValue métodos e SetValue no objeto de dependência de destino, respectivamente. Os designers podem aceder à propriedade anexada chamando através do invólucro de método ou fazendo uma chamada direta para o objeto de dependência de destino.
Para obter mais informações sobre propriedades anexadas, consulte Visão geral de propriedades anexadas.
Definir e usar recursos compartilhados
Você pode incluir seu controle no mesmo assembly que seu aplicativo, ou você pode empacotar seu controle em um assembly separado que pode ser usado em vários aplicativos. Na maioria das vezes, as informações discutidas neste tópico se aplicam independentemente do método usado. No entanto, há uma diferença que vale a pena notar. Quando você coloca um controle no mesmo assembly que um aplicativo, você é livre para adicionar recursos globais ao arquivo App.xaml. Mas um assembly que contém apenas controles não tem um Application objeto associado a ele, portanto, um arquivo App.xaml não está disponível.
Quando um aplicativo procura um recurso, ele examina três níveis na seguinte ordem:
O nível do elemento.
O sistema começa com o elemento que faz referência ao recurso e, em seguida, pesquisa recursos do pai lógico e assim por diante até que o elemento raiz seja alcançado.
O nível de aplicação.
Recursos definidos pelo Application objeto.
O nível do tema.
Os dicionários de nível de tema são armazenados em uma subpasta chamada 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 o procura no arquivo específico do tema e, em seguida, o procura em generic.xaml.
Quando o seu controlador está numa assemblagem à parte do aplicativo, deve colocar os seus recursos globais ao nível do elemento ou ao nível do tema. Ambos os métodos têm suas vantagens.
Definindo recursos no nível do elemento
Você pode definir recursos compartilhados no nível do elemento criando um dicionário de recursos personalizado e mesclando-o com o dicionário de recursos do seu controle. Quando você usa esse método, você pode nomear seu arquivo de recurso como quiser, e ele pode estar na mesma pasta que seus controles. Recursos no nível do elemento também podem usar cadeias de caracteres simples como chaves. O exemplo a seguir cria um ficheiro de recurso LinearGradientBrush chamado Dictionary1.xaml.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://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 definir seu dicionário, você precisa mesclá-lo com o dicionário de recursos do seu controle. Você pode fazer isso usando XAML ou código.
O exemplo a seguir mescla um dicionário de recursos 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 cada vez que você faz referência a ele. Por exemplo, se você tiver 10 controles personalizados em sua biblioteca e mesclar os dicionários de recursos compartilhados para cada controle usando XAML, criará 10 objetos idênticos ResourceDictionary . Você pode evitar isso criando uma classe estática que mescla os recursos no código e retorna o ResourceDictionary.
O exemplo a seguir cria uma classe que retorna um elemento 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 InitializeComponent. Como o SharedDictionaryManager.SharedDictionary é uma propriedade estática, o ResourceDictionary é criado apenas uma vez. Como o dicionário de recursos foi mesclado antes InitializeComponent de ser chamado, os recursos estão disponíveis para o controle em seu arquivo XAML.
public NumericUpDown()
{
this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
InitializeComponent();
}
Definindo recursos no nível do tema
WPF permite que você crie recursos para diferentes temas do Windows. Como um autor de controle, você pode definir um recurso para um tema específico para alterar a aparência do controle, dependendo do tema em uso. Por exemplo, a aparência de um Button no tema clássico do Windows (o tema padrão para o Windows 2000) difere de um Button no tema Windows Luna (o tema padrão para o Windows XP) porque o Button usa um diferente ControlTemplate para cada tema.
Os recursos específicos de um tema são mantidos em um dicionário de recursos com um nome de arquivo específico. Esses arquivos devem estar em uma pasta chamada Themes que é uma subpasta da pasta que contém o controle. A tabela a seguir lista os arquivos de dicionário de recursos e o tema associado a cada arquivo:
| Nome do arquivo do dicionário de recursos | Tema do Windows |
|---|---|
Classic.xaml |
Aparência clássica do Windows 9x/2000 no Windows XP |
Luna.NormalColor.xaml |
Tema azul padrão no Windows XP |
Luna.Homestead.xaml |
Tema Olive no Windows XP |
Luna.Metallic.xaml |
Tema Silver no Windows XP |
Royale.NormalColor.xaml |
Tema padrão no Windows XP Media Center Edition |
Aero.NormalColor.xaml |
Tema padrão no Windows Vista |
Não é necessário definir um recurso para cada tema. Se um recurso não estiver definido para um tema específico, o controlo verifica Classic.xaml para o recurso. Se o recurso não estiver definido no arquivo que corresponde ao tema atual ou no Classic.xaml, o controle usará o recurso genérico, que está em um arquivo de dicionário de recursos chamado generic.xaml. O generic.xaml arquivo está localizado na mesma pasta que os arquivos de dicionário de recursos específicos do tema. Embora generic.xaml não corresponda a um tema específico do Windows, ainda é um dicionário de nível de tema.
O controle personalizado NumericUpDown do C# ou Visual Basic com suporte para automação de tema e interface do usuário contém dois dicionários de recursos para o controle NumericUpDown: um está em generic.xaml e o outro está em Luna.NormalColor.xaml.
Quando se coloca um ControlTemplate em qualquer um dos arquivos de dicionário de recursos específicos do tema, deve criar-se um construtor estático para o controle e chamar-se o método OverrideMetadata(Type, PropertyMetadata) no DefaultStyleKey, como demonstrado no exemplo seguinte.
static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
Definição e referenciação de chaves para recursos temáticos
Ao definir um recurso no nível do elemento, você pode atribuir uma cadeia de caracteres como sua chave e acessar o recurso por meio da cadeia de caracteres. Ao definir um recurso no nível do tema, você deve usar a ComponentResourceKey como chave. O exemplo a seguir define um recurso em generic.xaml.
<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 ao recurso especificando o ComponentResourceKey como a chave.
<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>
Especificando 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 fazer isso adicionando o ThemeInfoAttribute ao assembly que contém o controle. O ThemeInfoAttribute tem uma GenericDictionaryLocation propriedade que especifica o local dos recursos genéricos e uma ThemeDictionaryLocation propriedade que especifica o local dos recursos específicos do tema.
O exemplo a seguir define as propriedades GenericDictionaryLocation e ThemeDictionaryLocation para SourceAssembly, a fim de especificar que os recursos genéricos e específicos do tema estão no mesmo assembly que o controle.
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
ResourceDictionaryLocation.SourceAssembly)]
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>
Ver também
.NET Desktop feedback