Compartilhar via


Visão geral sobre criação de controles

A extensibilidade do modelo de controles do Windows Presentation Foundation (WPF) reduz bastante a necessidade de se criar um novo controle. No entanto, em alguns casos você ainda pode precisar criar um controle personalizado. Este tópico aborda os recursos que minimizam sua necessidade de criar um controle personalizado e os diferentes modelos de criação de controles no Windows Presentation Foundation (WPF). Este tópico também demonstra como criar um novo controle.

Este tópico contém as seguintes seções.

  • Alternativas para gravar um novo controle
  • Modelos para criação de controles
  • Introdução a criação de controles
  • Herdar de UserControl vs.Usando um ControlTemplate
  • Tópicos relacionados

Alternativas para gravar um novo controle

Historicamente, se você quisesse obter uma experiência personalizada a partir de um controle existente, você era limitado a alterar as propriedades padrão do controle, como largura da borda, cor do plano de fundo e tamanho da fonte. Se você quis estender a aparência ou comportamento de um controle além desses parâmetros predefinidos, você precisará criar um novo controle, geralmente herdar de um controle existente e substituindo o método responsável por desenhar o controle. Embora ainda que seja uma opção, WPF permite a você personalizar os controles existentes usando o seu modelo de conteúdo avançado, 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.

  • **Conteúdo rico.**Muitos dos controles padrão do WPF suportam conteúdo rico. Por exemplo, a propriedade conteúda de um Button é do tipo Object, teoricamente, assim, nada pode ser exibido em uma 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 para criar um novo controle ou modificar um controle existente para oferecer suporte a uma visualização complexa. Para obter mais informações sobre o modelo de conteúdo para Button e outros controles, consulte Visão geral de Controles de Modelos de Conteúdo. Para obter mais informações sobre outros modelos no conteúdo WPF, consulte Modelos de conteúdo.

  • Estilos. Um Style é uma coleção de valores que representam propriedades para um controle. Usando estilos você pode criar uma representação reutilizável da aparência e do comportamento desejados para um controle sem escrever um novo controle. Por exemplo, suponha que você deseja que todos os seus TextBlock controles para que a fonte Arial, vermelho com um dimensionar de fonte de 14. Você pode criar um estilo sistema autônomo um recurso e conjunto sistema autônomo propriedades adequadas de acordo. Em seguida, cada TextBlock Se você adiciona 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 disso, consulte Visão geral sobre Templating de dados. Como personalizar a aparência dos dados, um DataTemplate pode incluir elementos de interface de usuário, que oferece a você muita flexibilidade nas interfaces do usuário personalizadas. Por exemplo, usando por um DataTemplate, você pode criar um ComboBox em que cada item contém uma caixa de seleção.

  • Modelos de controle. Muitos controles de WPF Use um ControlTemplate Para definir a estrutura e aparência, que separa a aparência de um controle da funcionalidade do controle do controle. Drasticamente você pode alterar a aparência de um controle, redefinindo seu ControlTemplate. Por exemplo, suponha que você deseja que um controle que se parece com uma alerta. Esse controle tem uma interface de usuário simples e funcionalidade. O controle é três círculos, apenas um dos quais pode estar aceso cada vez. Depois de alguns reflexão, talvez você perceba que um RadioButton oferece a funcionalidade de apenas uma seja selecionada em uma time, mas a aparência padrão das RadioButton não se parece em nada com as luzes em uma alerta. Porque o RadioButton usa um modelo de controle para definir sua aparência, é fácil de redefinir o ControlTemplate atendam aos requisitos do controle e use os botões de opção para tornar a alerta.

    ObservaçãoObservação:

    Embora um RadioButton pode usar um DataTemplate, um DataTemplate não é suficiente neste exemplo. The DataTemplate Define a aparência do conteúdo de um controle. No caso de um RadioButton, o conteúdo é tudo o que é exibido à direita do círculo indica se a RadioButton está selecionada. No exemplo do alerta, o botão de opção precisa ser apenas um círculo pode "acender." Como o requisito de aparência para o alerta é tão diferente do que a aparência padrão do RadioButton, é necessário redefinir o ControlTemplate. Em geral um DataTemplate é usado para definição de conteúdo (ou dados) de um controle e um ControlTemplate é usado para definir como um controle está estruturado.

  • Acionadores A Trigger permite que você dinamicamente alterar a aparência e o comportamento de um controle sem criar um novo controle. Por exemplo, suponha que você tem vários ListBox controles em seu aplicativo e quiser que os itens em cada ListBox a ser negrito e vermelho quando elas estão selecionadas. Seu primeiro instinto seria criar uma classe que herda de ListBox e substituem a OnSelectionChanged método para alterar a aparência do item selecionado, mas uma abordagem melhor é adicionar um disparar para um estilo de um ListBoxItem que altera a aparência do item selecionado. Um disparar permite que você altere os valores de propriedade ou tomar ações com base no valor de uma propriedade. An EventTrigger permite que você execute ações quando ocorrer 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 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.

Modelos para criação de controles

O modelo de conteúdo avançado, estilos, modelos e disparadores minimizar a necessidade de criar um novo controle. No entanto, se for necessário criar um novo controle, é importante entender o controle de diferente criação modelos em WPF. WPF fornece três modelos Geral para criar um controle, cada um deles fornece um conjunto diferente de recursos e o nível de flexibilidade. As classes base para os três modelos são UserControl, Control, e FrameworkElement.

Derivando de UserControl

A maneira mais simples de criar um controle em WPF é derivar de UserControl. Quando você constrói um controle que herda de UserControl, você adicionar os componentes existentes para o UserControl, nomeie os componentes e manipuladores de eventos em de referência Extensible Application Markup Language (XAML). Você pode fazer referência os elementos nomeados e definir o evento manipuladores do código. Este modelo de desenvolvimento é muito semelhante do modelo usado para desenvolvimento de aplicativos no WPF.

Se criado corretamente, um UserControl pode tirar proveito dos benefícios de conteúdo rico, estilos e acionadores. No entanto, se o controle herda de UserControl, as pessoas que usam o controle não poderão usar um DataTemplate ou ControlTemplate Para personalizar sua aparência. É necessário derivar a partir de Control classe ou 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 a derivação de UserControl Se todos estes procedimentos se aplicam:

  • Você deseja criar o controle de forma semelhante a como você cria um aplicativo.

  • Seu controle consiste somente em componentes existentes.

  • Você não precisará de suporte complexo a personalização.

Derivando de Control

Derivar da classe Control é o modelo usado pela maioria dos controles WPF existentes. Quando você cria um controle que herda a Control classe, você define sua aparência usando modelos. Fazendo isso, você separar a lógica operacional da representação visual. Você também pode garantir a desassociação de UI e lógica usando comandos e sistema autônomo ligações em vez de eventos e evitando elementos de referência no ControlTemplate sempre que possível.  Se a interface do usuário e lógica do seu controle são desmembrados corretamente, um usuário do seu controle poderá redefinir do controle ControlTemplate Para personalizar sua aparência. Embora criar uma personalizada Control não é tão simples quanto a criação de um UserControl, um personalizado Control fornece mais flexibilidade.

Benefícios de derivar de Control

Considere a derivação de Control em vez de usar o UserControl classe se algum dos aplicar seguintes:

  • Você deseja que a aparência do seu controle para ser personalizável via o ControlTemplate.

  • Você deseja que o controle suporta temas diferentes.

Derivando de FrameworkElement

Controles que derivam de UserControl ou Control dependem de 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á ocasiões em que quando aparência de um controle requer mais do que a funcionalidade de composição de elemento simples. Para essas situações, um componente baseado em FrameworkElement é a opção correta.

Há dois métodos padrão para a construção FrameworkElement-componentes baseados em: renderização direto e composição de elemento personalizado. Renderização direta envolve sobrescrever o método OnRender método de FrameworkElement e fornecer operações DrawingContext que definam explicitamente os elementos visuais do componente. Este é o método usado por Image e Border. Composição de elemento personalizado envolve o uso de objetos do tipo Visual para redigir a aparência do seu componente. Para um exemplo, consulte Using DrawingVisual Objects. Track é um exemplo de um controle em WPF composição de elemento personalizado que usa. Também é possível misturar renderização direto e composição de elemento personalizado no mesmo controle.

Benefícios de derivar de FrameworkElement

Considere a derivação de FrameworkElement Se houver aplicar seguintes:

  • Você deseja ter controle preciso sobre a aparência do seu controle além do que é fornecido pela composição de elemento simples.

  • Você deseja definir a aparência de seu controle definindo sua própria lógica de renderização.

  • Você deseja compor elementos existentes em novas maneiras, que vão além do que é possível com UserControl e Control.

Introdução a criação de controles

Conforme discutido anteriormente, um dos recursos mais poderosos de WPF é a capacidade para ir além de configurar 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 disparar disponibilizados pela WPF sistema de propriedades e o WPF sistema de eventos. Se você implementar sistema autônomo propriedades de dependência e eventos roteados no seu controle, sistema autônomo usuários do seu controle personalizado podem usar esses recursos sistema autônomo fariam em um controle que é fornecido com WPF, independentemente do modelo de você usar para criar o controle personalizado. 

Use propriedades de dependência

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 sistema autônomo o valor da propriedade.

  • Anime a propriedade.

Se você desejar que uma propriedade de seu controle para oferecer suporte a essa funcionalidade, você deve implementar sistema autônomo uma propriedade de dependência. O exemplo a seguir define uma propriedade de dependência chamada Value fazendo o seguinte:

  • Define a DependencyProperty identifier called ValueProperty as a public static readonly field.

  • Registrar 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 para a propriedade. Os metadados contém valor de padrão da propriedade, um CoerceValueCallback e um PropertyChangedCallback.

  • Definir um CLRpropriedade "empacotador" chamada Value, que é o mesmo nome que é usado para registrar a propriedade de dependência com a implementação da 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 pode ignorar os acessadores e telefonar GetValue e SetValue diretamente. Por exemplo, quando uma propriedade estiver vinculada a uma fonte de dados set o acessador não é chamado. Em vez de adicionar lógica adicional para get e conjunto acessadores, use o ValidateValueCallback, CoerceValueCallback, e PropertyChangedCallback delega ao responder ou verificar o valor quando for alterado. 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. CoerceValue garante que Value é maior ou igual a MinValue e menor ou igual a MaxValue.

  • Definir um método para o PropertyChangedCallback, chamado OnValueChanged. OnValueChanged cria um RoutedPropertyChangedEventArgs<T> objeto e o prepara para elevar o ValueChanged evento roteado. 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);
}

Para obter mais informações, consulte Propriedades de Dependência Personalizada.

Uso roteados eventos

Assim sistema autônomo Propriedades de dependência estender a noção de CLR propriedades com funcionalidade adicional, eventos roteados estender a noção de padrão de CLR eventos. Quando você cria um novo WPF controle, é também recomendável implementar seu evento sistema autônomo um evento roteado sistema autônomo um evento roteado oferece suporte para o seguinte comportamento:

  • Eventos podem ser tratados em um pai de vários controles. Se um evento for um evento de bubbling, um único pai na árvore de elemento pode assinar o evento. Em seguida aos 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 ele está incluído um DataTemplate), o desenvolvedor do aplicativo pode definir o manipulador de eventos para evento do controle no ListBox. Sempre que o evento ocorre em qualquer um dos controles, o manipulador de eventos é chamado.

  • Eventos roteados podem ser usados em um EventSetter, que permite que os desenvolvedores de aplicativos especifiquem o manipulador de um evento em um estilo.

  • Eventos roteados podem ser usados em um EventTrigger, que é útil para animar propriedades usando XAML. Para obter mais informações, consulte Revisão de Animação.

O exemplo a seguir define um evento roteado, fazendo o seguinte:

  • Define a RoutedEvent identifier called ValueChangedEvent as a public static readonly field.

  • Registra o evento roteado chamando o EventManager.RegisterRoutedEvent método. O exemplo especifica as informações a seguir quando chama RegisterRoutedEvent:

    • O nome do evento é ValueChanged.

    • A estratégia de roteamento é Bubble, que significa que um manipulador de eventos na origem (o objeto que gera o evento) é chamado primeiro e, em seguida, são chamados de manipuladores de eventos em elementos de pai da fonte em sucessão, começando com o manipulador de eventos no elemento pai mais próximo.

    • O tipo do manipulador de eventos é RoutedPropertyChangedEventHandler<T>, construído com uma Decimal Digite.

    • O tipo de proprietário de evento é NumericUpDown.

  • Declarar um evento público chamado ValueChanged e inclui declarações de assessor de evento. O exemplo chama AddHandler no add declaração de acessador e RemoveHandler no remove declaração de acessador para usar o WPF evento serviços.

  • Criar um método virtual protegido chamado OnValueChanged que dispara a ValueChanged evento.

/// <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);
}

Para obter mais informações, consulte Visão geral sobre eventos roteados e Como: Criar um evento roteado personalizado.

Usar vinculação

Para desacoplar a interface do usuário do seu controle de sua lógica, considere a possibilidade de usar a vinculação de dados. Isso é particularmente importante se você definir a aparência do seu controle, usando um ControlTemplate. Quando você usa a vinculação de dados, talvez seja possível eliminar a necessidade para fazer referência a partes específicas da interface do usuário a partir do código. É uma mercadoria idéia evitar fazendo referência a elementos que estão no ControlTemplate porque quando o código faz referência a elementos que estão na ControlTemplate e o ControlTemplate é alterado, as necessidades de elemento referenciado a serem incluídos no novo ControlTemplate.

O exemplo a seguir atualiza o TextBlock da NumericUpDown controle, atribuir um nome para ele e fazendo 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 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>

Para obter mais informações, consulte Revisão de Associação de Dados.

Definir e usar comandos

Considere a possibilidade de definir e usar comandos em vez de manipulação de eventos para fornecer funcionalidade. Quando você usa evento manipuladores em seu controle, a ação feita dentro a evento manipulador está inacessível para os aplicativos. Quando você implementa comandos no seu controle, os aplicativos podem acessar a funcionalidade que seria não disponível.

O exemplo a seguir é parte de um controle que manipula o evento clicar para dois botões alterar o valor do NumericUpDown controle. Independentemente de se o controle é um UserControl ou um Control com um ControlTemplate, a interface do usuário e a lógica são rigidamente acoplados como o controle usa manipuladores de eventos.

  <RepeatButton Name="upButton" Click="upButton_Click" 
                  Grid.Column="1" Grid.Row="0">Up</RepeatButton>
                  
    <RepeatButton Name="downButton" Click="downButton_Click" 
                  Grid.Column="1" Grid.Row="1">Down</RepeatButton>
private void upButton_Click(object sender, EventArgs e)
{
        Value++;
}

private void downButton_Click(object sender, EventArgs e)
{
        Value--;
}

O exemplo a seguir define dois comandos que alteram o valor de NumericUpDown controle.

public static RoutedCommand IncreaseCommand
{
    get
    {
        return _increaseCommand;
    }
}
public static RoutedCommand DecreaseCommand
{
    get
    {
        return _decreaseCommand;
    }
}

private static void InitializeCommands()
{
    _increaseCommand = new RoutedCommand("IncreaseCommand", typeof(NumericUpDown));
    CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown), 
                            new CommandBinding(_increaseCommand, OnIncreaseCommand));
    CommandManager.RegisterClassInputBinding(typeof(NumericUpDown), 
                            new InputBinding(_increaseCommand, new KeyGesture(Key.Up)));

    _decreaseCommand = new RoutedCommand("DecreaseCommand", typeof(NumericUpDown));
    CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown), 
                            new CommandBinding(_decreaseCommand, OnDecreaseCommand));
    CommandManager.RegisterClassInputBinding(typeof(NumericUpDown), 
                            new InputBinding(_decreaseCommand, new KeyGesture(Key.Down)));
}

private static void OnIncreaseCommand(object sender, ExecutedRoutedEventArgs e)
{
    NumericUpDown control = sender as NumericUpDown;
    if (control != null)
    {
        control.OnIncrease();
    }
}
private static void OnDecreaseCommand(object sender, ExecutedRoutedEventArgs e)
{
    NumericUpDown control = sender as NumericUpDown;
    if (control != null)
    {
        control.OnDecrease();
    }
}

protected virtual void OnIncrease()
{
    Value++;
}
protected virtual void OnDecrease()
{
    Value--;
}

private static RoutedCommand _increaseCommand;
private static RoutedCommand _decreaseCommand;

Elementos no modelo podem então consultar sistema autônomo comandos, conforme mostrado no exemplo a seguir.

<RepeatButton 
    Command="{x:Static local:NumericUpDown.IncreaseCommand}"  
    Grid.Column="1" Grid.Row="0">Up</RepeatButton>
<RepeatButton 
    Command="{x:Static local:NumericUpDown.DecreaseCommand}"  
    Grid.Column="1" Grid.Row="1">Down</RepeatButton>

Agora os aplicativos podem referenciar as ligações para acesso a funcionalidade que estava inacessoible quando o controle usado em manipuladores de eventos. Para obter mais informações sobre comandos, consulte Visão geral de Comando.

Especificar se um elemento necessário de um ControlTemplate

As seções anteriores explicam como usar a vinculação de dados e comandos para que um controle não faz referência a elementos no seu ControlTemplate no código. No entanto, pode haver ocasiões em que é inevitável fazendo referência a um elemento. Se essa situação ocorrer, você deverá aplicar o TemplatePartAttribute em seu controle. Esse atributo informa os autores do modelo dos tipos e nomes de elementos no ControlTemplate. Não, todos os elementos de um ControlTemplate precisa ser nomeados em um TemplatePartAttribute. Na verdade, menos elementos nomeados, melhor. Mas se você consultar o elemento no código, você deve usar o TemplatePartAttribute.

Para obter mais informações sobre como criar um controle que usa um ControlTemplate, consulte Diretrizes para criação de controles estilizados.

Design para designers

Para receber suporte para controles personalizados do WPF no Windows Presentation Foundation (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 como desenvolver para o WPF Designer, consulte WPF Designer.

Propriedades de dependência

Be sure to implement CLR get and set accessors as described earlier, in "Use Dependency Properties." Os designers podem usá-lo para detectar a presença de uma propriedade de dependência, mas, como WPF e os clientes do controle, não são necessárias para telefonar acessadores ao obter ou definir a propriedade.

Propriedades anexadas

Você deve implementar propriedades anexadas nos controles personalizados usando as diretrizes a seguir:

  • Have a public static readonly DependencyProperty of the form PropertyNameProperty that was creating using the RegisterAttached method. O nome da propriedade que é passado para RegisterAttached deve corresponder ao PropertyName .

  • Implementar um par de métodos CLR public static chamados SetPropertyName e GetPropertyName. Ambos os métodos devem aceitar uma classe derivada de DependencyProperty como o primeiro argumento. O método SetPropertyName também aceita um argumento cujo tipo corresponde ao tipo de dados registrado para a propriedade. O método GetPropertyName deve retornar um valor do mesmo tipo. Se o SetPropertyName método está ausente, a propriedade é somente leitura.

  • SetPropertyName e GetPropertyName devem redirecionar-se diretamente para os métodos GetValue e SetValue do objeto alvo da dependência, respectivamente. Os designers podem acessar a propriedade anexada chamando por meio do método wrapper ou fazendo uma telefonar direta para o objeto de dependência de destino.

Para obter mais informações sobre propriedades anexadas, consulte Attached Properties Overview.

Definindo e usando recursos compartilhados para o controle

Você pode incluir seu controle no mesmo assembly sistema autônomo seu aplicativo, ou você pode empacotar seu controle em um assembly separado que pode ser usado em vários aplicativos. Em sua maioria, as informações discutidas neste tópico se aplicam independentemente do método que é usar. No entanto, há uma diferença digno de nota. Quando você coloca um controle no mesmo assembly sistema autônomo um aplicativo, você é disponível para adicionar recursos global para o arquivo app.xaml. Mas um assembly que contém somente controles não possui um Application objeto associado a ela, portanto, um arquivo app.xaml não está disponível.

Quando um aplicativo procura por um recurso, ele procura em três níveis na seguinte ordem:

  1. O nível de elemento — O sistema começa com o elemento que faz referência o recurso e procura recursos do pai lógico e assim por diante até que o elemento raiz seja atingido.

  2. Nível do aplicativo — recursos definidos pelo Application objeto.

  3. O nível de tema — tema nível dicionários são armazenados em uma subpasta chamada temas. Os arquivos na pasta Themes correspondem aos temas. Por exemplo, talvez seja necessário 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 procurará um recurso no nível de temas, ele primeiro procura por ele no arquivo de tema específico e, em seguida, procura por ele em generic.xaml.

Quando o controle está em um assembly separado do aplicativo, você deve colocar seus recursos global no nível do elemento ou no nível do tema. Os dois métodos têm suas vantagens.

Definir recursos no nível do elemento

Você pode definir recursos compartilhados no nível do elemento, criando um dicionário de recurso personalizados e a mesclagem com dicionário de recurso do controle. Quando você usa esse método, você pode chamar seu arquivo de recurso que quiser e ele pode ser na mesma pasta sistema autônomo seus controles. Recursos no nível do elemento também podem usar cadeias de caracteres simples sistema autônomo chaves. O exemplo a seguir cria um LinearGradientBrush arquivo de recurso chamado 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á mesclagem com 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 faça referência a ele. Por exemplo, se você tiver 10 controles personalizados em sua biblioteca e mescle os dicionários de recurso compartilhado para cada controle usando XAML, criar 10 idênticos ResourceDictionary objetos. Você pode evitar isso através da criação de 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 compartilhada 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 é criada apenas uma vez. Porque o dicionário de recurso foi mesclado antes de InitializeComponent foi chamado, os recursos estão disponível para o controle na sua XAML arquivo.

public NumericUpDown()
{
    this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
    InitializeComponent();

}

Definir recursos no nível do tema

WPF permite a criar de recursos para diferentes temas do Windows. sistema autônomo o autor de um controle, você pode definir um recurso para um tema específico alterar a aparência do controle dependendo o tema é usado. Por exemplo, a aparência de um Button Windows Classic tema (o tema padrão para o Windows 2000) difere de um Button no tema Luna do Windows (o tema padrão para o Windows XP) porque o Button usa um diferente ControlTemplate para cada tema.

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 que é 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:

nome de arquivo de dicionário de recurso

Tema do Windows

Classic.XAML

“ Clássico ” Windows 9 x / 2000 procura no Windows XP

Luna.NormalColor.XAML

Tema padrão azul no Windows XP

Luna.Homestead.XAML

Verde-oliva tema no Windows XP

Luna.metallic.XAML

Tema prateado no Windows XP

Royale.NormalColor.XAML

Tema padrão no Windows XP mídia centralizar edição

Aero.NormalColor.XAML

Tema padrão no Windows Vista

Você não precisa definir um recurso para cada tema. Se um recurso não está definido para um tema específico, o controle usará o recurso genérico, em um arquivo de dicionário de recurso chamado generic.XAML na mesma pasta sistema autônomo os arquivos de dicionário de recurso específico do tema. Embora generic.XAML não corresponde a um tema específico do Windows, ainda é um dicionário de nível de tema.

Controlarar de NumericUpDown Personalizar com tema e exemplo de suporte de automação da interface do usuário contém dois dicionários de recurso para o NumericUpDown controle: uma é em generic.xaml e um está sendo Luna.NormalColor.xaml. Você pode executar o aplicativo e comutador entre o tema prata 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 Aero.NormalColor.xaml e comutador entre dois temas, sistema autônomo o tema clássico do Windows e o tema padrão para o Windows Vista.)

Quando você coloca um ControlTemplate em qualquer um dos arquivos de dicionário de recursos específicos do tema, você deve criar um construtor estático para o controle e a telefonar a OverrideMetadata(Type, PropertyMetadata) método na DefaultStyleKey, conforme mostrado no exemplo a seguir.

static NumericUpDown()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
               new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}

Definindo e referenciar as teclas para obter os recursos do tema

Quando você define um recurso no nível do elemento, você pode atribuir uma seqüência de caracteres sistema autônomo sua chave e acessar o recurso através da seqüência de caracteres. Ao definir um recurso no nível do tema, você deve usar um ComponentResourceKey sistema autônomo a chave. O exemplo a seguir define um recurso em generic.xaml.

O exemplo a seguir faz referência o recurso especificando o ComponentResourceKey sistema autônomo a chave.

Especificar a localização de 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 que adicionando o ThemeInfoAttribute para o assembly que contém o controle. The 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 sistema autônomo recursos genéricos e específicos de tema estão no mesmo assembly sistema autônomo o controle.

[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, 
           ResourceDictionaryLocation.SourceAssembly)]

Herdar de UserControl vs.Usando um ControlTemplate

Várias Exemplos demonstram métodos diferentes para escrever e empacotamento de NumericUpDown controle. In UserControl NumericUpDown com DependencyProperty e exemplo RoutedEvent, NumericUpDown herda do UserControl; em Controlarar de NumericUpDown Personalizar com tema e exemplo de suporte de automação da interface do usuário, NumericUpDown herda do Control e usa um ControlTemplate. Esta seção descreve algumas das diferenças entre os dois brevemente e explica por que o controle que usa um ControlTemplate é mais extensível.

A primeira grande diferença é que o NumericUpDown que herda de UserControl Não use um ControlTemplate e o controle que herda diretamente Control não. O exemplo a seguir mostra o XAML o controle herda a partir de UserControl. sistema autônomo você pode ver, a XAML é muito semelhante ao que você pode ver quando você cria um aplicativo e começar com um Window ou Page.

<!--XAML for NumericUpDown that inherits from UserControl.-->
<UserControl x:Class="MyUserControl.NumericUpDown"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyUserControl">
    <Grid Margin="3">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <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>

        <RepeatButton Name="upButton" Click="upButton_Click" 
                      Grid.Column="1" Grid.Row="0">Up</RepeatButton>

        <RepeatButton Name="downButton" Click="downButton_Click" 
                      Grid.Column="1" Grid.Row="1">Down</RepeatButton>

    </Grid>
</UserControl>

O exemplo a seguir mostra o ControlTemplate o controle herda a partir de Control. Observe que o ControlTemplate se parece com o XAML no UserControl, com apenas algumas diferenças na sintaxe.

<!--ControlTemplate for NumericUpDown that inherits from
    Control.-->
<Style TargetType="{x:Type local:NumericUpDown}">
  <Setter Property="HorizontalAlignment" Value="Center"/>
  <Setter Property="VerticalAlignment" Value="Center"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:NumericUpDown}">
        <Grid Margin="3">
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>

          <Border BorderThickness="1" BorderBrush="Gray" 
                  Margin="2" Grid.RowSpan="2" 
                  VerticalAlignment="Center" HorizontalAlignment="Stretch">

            <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value}" 
                       Width="60" TextAlignment="Right" Padding="5"/>
          </Border>

          <RepeatButton Command="{x:Static local:NumericUpDown.IncreaseCommand}"  
                        Grid.Column="1" Grid.Row="0">Up</RepeatButton>

          <RepeatButton Command="{x:Static local:NumericUpDown.DecreaseCommand}"
                        Grid.Column="1" Grid.Row="1">Down</RepeatButton>

        </Grid>

      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

A maior diferença nos dois exemplos anteriores é que aquela que usa o ControlTemplate tem uma aparência personalizável e aquele que herda de UserControl não ocorre. No caso em que NumericUpDown herda do UserControl, um desenvolvedor de aplicativo não pode fazer nada para alterar a aparência do controle. Na verdade, mesmo que o NumericUPDown tem um ControlTemplate propriedade (porque UserControl herda do Control) se alguém tentar defini-la, uma exceção irá ocorrer em time de execução. Por Outros lado, um desenvolvedor de aplicativo que usa o NumericUpDown que herda de Control está disponível para criar um novo ControlTemplate para o controle. Por exemplo, alguém poderia criar um ControlTemplate que colocados nos botões para a esquerda e direita das TextBlock em vez de acima e abaixo dela.

A diferença entre as duas abordagens é evidente na diferença sintática nos exemplos anteriores. O controle que usa um ControlTemplate Define o Template propriedade em um Style para NumericUpDown. Esse é um método comum de criar modelos de controle. Definindo o Template propriedade em um estilo, você estará especificando que todas as instâncias do controle usará que ControlTemplate. aplicativo desenvolvedores são disponível para alterar o Template propriedade de um NumericcUpDown Para personalizar sua aparência. Em contraste, a XAML o controle que herda de UserControl preenche o Content propriedade de NumericUpDown (<UserControl.Content> implicado na XAML). Se um desenvolvedor de aplicativo não é possível alterar o Content propriedade, o NumericUpDown não é utilizável.

Outra diferença entre os exemplos é como os controles respondem à Backup and Para baixo botões. O controle que herda de UserControl manipula o evento clicar e o controle que usa um ControlTemplate Implementa comandos e liga para os comandos nos seus ControlTemplate. sistema autônomo resultado, um desenvolvedor de aplicativo que cria um novo ControlTemplate para o NumericUpDown também pode BIND sistema autônomo comandos e mantêm a funcionalidade do controle. Se o ControlTemplate tratado o evento clicar em vez de vinculação de comandos, um desenvolvedor de aplicativos precisa implementar manipuladores de eventos ao criar uma nova ControlTemplate, assim, quebrando o encapsulamento da NumericUpDown.

Outra diferença é a sintaxe do vínculo entre o Text propriedade das TextBlock e o Value propriedade. No caso do UserControl, a vinculação Especifica que o RelativeSource é o pai NumericUpDown controlar e vincula o Value propriedade. No caso do ControlTemplate, o RelativeSource é o controle ao qual pertence o modelo. Alcançar a mesma coisa mas vale a pena mencionar que a sintaxe de ligação é diferente nos dois exemplos.

In Controlarar de NumericUpDown Personalizar com tema e exemplo de suporte de automação da interface do usuário, o NumericUpDown controle consiste em um assembly que é separado do aplicativo e define e utiliza recursos do tema, mas em UserControl NumericUpDown com DependencyProperty e exemplo RoutedEvent, o NumericUpDown controle está no mesmo assembly sistema autônomo o aplicativo e não defina ou usar sistema autônomo recursos de nível de tema.

Finalmente, Controlarar de NumericUpDown Personalizar com tema e exemplo de suporte de automação da interface do usuário mostra como criar um AutomationPeer para o NumericUpDown controle. Para obter mais informações sobre suporte Automação da Interface do Usuário para controles personalizados, consulte Automação da interface do usuário de um controle personalizado do WPF.

Consulte também

Conceitos

URIs de Pacotes no Windows Presentation Foundation

Outros recursos

WPF Designer

Control Customization

Exemplos de personalização de Controlarar