Desenvolvendo um controle personalizado usando XAML e C#

Talvez você já saiba que um dos recursos mais avançados da plataforma XAML do Windows 8 é a flexibilidade que fornece para criar controles personalizados. O XMAL fornece recursos como Propriedades de Dependência e Modelos de Controle para facilitar a criação de controles personalizáveis repletos de recursos.

Na última postagem, “Criando um controle personalizado com o WinJS (Biblioteca do Windows para JavaScript)”, Jordan Matthiesen orientou você através da criação de um controle personalizado HelloWorld. Nesta postagem, vou orientá-lo através da criação desse mesmo controle em Xaml. Apresentarei técnicas e conceitos que permitirão a você criar controles personalizados reutilizáveis e mostrarei como criar um modelo para o estilo desses controles. Introduzirei conceitos como as Propriedades de Dependência e usarei um arquivo Generic.xaml personalizado para criar um estilo implícito que defina um modelo de controle padrão.

Um controle XAML simples

Primeiro, desenvolveremos o Hello World dos controles: uma classe que deriva do Windows.UI.XAML.Controls.Control. Crie um novo projeto no Visual Studio usando o modelo Projeto em Branco. Nomeie seu projeto como CustomControls. Adicione seu controle personalizado usando o assistente de modelo Novo Item.

O Visual Studio inclui um modelo de item para um controle modelo. Clique com o botão direito do mouse no projeto e selecione Adicionar -> Novo Item

O Visual Studio inclui um modelo de item para um controle modelo. Clique com o botão direito do mouse no projeto e selecione Adicionar -> Novo Item”

O modelo de item do controle modelo cria os arquivos e alguns códigos esqueleto para que seu controle personalizado seja iniciado

Selecione o item Controle Modelo e nomeie o controle como “HelloWorld.cs”

Este modelo cria esta classe:

 public class HelloWorld : Control
{
    public HelloWorld()    {
        this.DefaultStyleKey = typeof(HelloWorld);
    }
}

Neste breve bloco de código, especificamos dois detalhes muito importantes. O primeiro é que a classe HelloWorld deriva do Controle. O segundo é que a definição de DefaultStyleKey indica para a plataforma XAML que esta classe usa um estilo implícito. O modelo do controle modelo também adiciona uma pasta denominada Temas e cria nessa pasta um novo arquivo chamado Generic.xaml. Há mais informações sobre esse modelo nos Modelos de projeto.

Um estilo padrão do controle é definido em um arquivo Generic.xaml personalizado que a plataforma carrega automaticamente. Nesse arquivo, você define um estilo implícito, o que significa que podemos definir um estilo que será aplicado por padrão a todos os controles de um tipo específico. Adicione o XAML realçado ao novo arquivo Generic.xaml na pasta Temas:

 <ResourceDictionary
    xmlns="https://schemas.microsoft.com/winfx/2006/XAML/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/XAML"
    xmlns:local="using:CustomControls">

    <Style TargetType="local:HelloWorld">
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:HelloWorld">
                    <Border
                       Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                        <TextBlock Text="HelloWorld" 
                               FontFamily="Segoe UI Light"
                               FontSize="36"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style> 
</ResourceDictionary>

Essas poucas linhas do XAML definem um estilo que será aplicado por padrão a todas as instâncias do controle HelloWorld. Definimos um Modelo de Controle e esse modelo especifica que esse controle é simplesmente um TextBlock com o texto “HelloWorld”.

Agora, vá até seu arquivo MainPage.xaml e adicione a próxima marcação. Você deve incluir a designação “local:" para deixar o XAML saber em que namespace encontrará a classe HelloWorld. A designação “local:" é definida na parte superior do arquivo XAML.

 <local:HelloWorld />

Ao executar o seu aplicativo, você verá que o controle foi carregado e exibe o texto “Hello, World!”.

Definindo opções de controle

Os controles se tornam mais úteis e reutilizáveis quando adicionamos opções configuráveis. Vamos adicionar um opção para permitir que o controle seja definido para intermitência.

Para adicionar essa opção, acrescentamos uma Propriedade de Dependência (DP) ao controle. Você pode saber muito mais sobre DPs na Visão geral de Propriedades de Dependência. Há um trecho do Visual Studio que facilita muito a adição de DPs. Com o cursor abaixo do construtor, digite “propdp” e pressione Tab duas vezes. Você pode preencher as especificações percorrendo cada parâmetro no trecho.

 public class HelloWorld : Control
{
    public HelloWorld()    {
        this.DefaultStyleKey = typeof(HelloWorld);
    }

    public bool Blink
    {
        get { return (bool)GetValue(BlinkProperty); }
        set { SetValue(BlinkProperty, value); }
    }

    // Using a DependencyProperty enables animation, styling, binding, etc.
    public static readonly DependencyProperty BlinkProperty =
        DependencyProperty.Register(
            "Blink",                  // The name of the DependencyProperty
            typeof(bool),             // The type of the DependencyProperty
            typeof(HelloWorld),       // The type of the owner of the DependencyProperty
            new PropertyMetadata(     // OnBlinkChanged will be called when Blink changes
                false,                // The default value of the DependencyProperty
                new PropertyChangedCallback(OnBlinkChanged)
            )
        );

    private DispatcherTimer __timer = null;
    private DispatcherTimer _timer
    {
        get
        {
            if (__timer == null)
            {
                __timer = new DispatcherTimer();
                __timer.Interval = new TimeSpan(0,0,0,0,500); // 500 ms interval
                __timer.Tick += __timer_Tick;
            }

            return __timer;
        }
    }

    private static void OnBlinkChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e
    )
    {
        var instance = d as HelloWorld;
        if (instance != null)
        {
            if (instance._timer.IsEnabled != instance.Blink)
            {
                if (instance.Blink)
                {
                    instance._timer.Start();
                }
                else
                {
                    instance._timer.Stop();
                }
            }
        }
    }

    private void __timer_Tick(object sender, object e)
    {
        DoBlink();
    }

    private void DoBlink()
    {
        this.Opacity = (this.Opacity + 1) % 2;
    }
}

De volta a MainPage.xaml, adicione a opção de configuração ao controle.

 <local:HelloWorld Blink="True" />

Execute o aplicativo e veja o controle piscando intermitente!

Adicionando suporte para eventos

Ao adicionar eventos aos controles, você pode aumentar a funcionalidade. Os eventos permitem que você obtenha interrupções do controle quando as ações ocorrem e depois execute um código que responda às ações. Vamos adicionar um evento que acione sempre as intermitências do controle.

 public class HelloWorld : Control
{
    
    ...
...
    private void __timer_Tick(object sender, object e)
    {
        DoBlink();
    }

    private void DoBlink()
    {
        this.Opacity = (this.Opacity + 1) % 2;
        OnBlinked();
    }

    public event EventHandler Blinked;

    private void OnBlinked()
    {
        EventHandler eh = Blinked;
        if (eh != null)
        {
            eh(this, new EventArgs());
        }
    }
}

De volta a MainPage.xaml, adicione uma propriedade x:Name ao elemento para que possamos recuperar a instância do controle posteriormente no código:

 <local:HelloWorld x:Name="HelloWorldWithEvents" Blink="True" />

Agora, em MainPage.xaml.cs, adicione um representante da função do ouvinte de eventos ao resultado da depuração de impressão quando o evento for acionado.

 HelloWorldWithEvents.Blinked += (object sender, EventArgs args) =>
{
    System.Diagnostics.Debug.WriteLine("HelloWorldWithEvents Blinked");
};

Ao executar esse código, você verá uma mensagem escrita a cada 500 milissegundos para a janela do Console de Saída no Visual Studio.

Expondo métodos públicos

Já escrevemos um método particular DoBlink para tornar o controle intermitente e agora divulgamos esse método para permitir que você tenha a opção de tornar o controle intermitente a qualquer momento. Tudo o que você precisa fazer é alterar a visibilidade do método DoBlink para público:

 public class HelloWorld : Control
{
    ...

    public void DoBlink()
    {
        this.Opacity = (this.Opacity + 1) % 2;
        OnBlinked();
    }

    ...
}

Agora, você pode chamar o método DoBlink público em C# code-behind, talvez em um manipulador de cliques de botão.

 private void Button_Click_1(object sender, RoutedEventArgs e)
{
    HelloWorldWithEvents.DoBlink();
}    

Reutilizando controles

Quando você deseja usar um controle somente em um único projeto ou para seu próprio uso pessoal, não há muito o que pensar em termos de empacotamento e distribuição. Mas se você quer desenvolver um controle reutilizável para compartilhar com outros desenvolvedores, existem algumas opções. Uma opção é criar um SDK de Extensão do Visual Studio que outros desenvolvedores possam instalar em suas máquinas e adicionar aos seus projetos. Para obter mais informações sobre como fazer isto, confira Criando um SDK usando C# ou Visual Basic.

Resumindo

Acabamos de trabalhar com os aspectos mais comuns de um controle que você desejará implementar:

  1. Levar o seu controle para uma página
  2. Inserir opções de configuração
  3. Expedir e responder a eventos
  4. Expor funcionalidade via métodos públicos

Assim, aqui estão as noções básicas dos controles personalizados XAML. A próxima etapa é encontrar alguma funcionalidade que você queira adicionar ao controle XAML existente e alterar o estilo ou subclassificar o controle para adicionar sua própria funcionalidade. Conforme fiquei mais à vontade com os controles personalizados, me senti muito mais seguro escrevendo aplicativos em XAML. Espero que você experimente seus próprios controle personalizados e faça a postagem de suas experiência online.

Espero que tenha achado esta postagem útil! Se tiver perguntas ao trabalhar com os seus próprios controles, acesse o Centro de Desenvolvimento do Windows e faça perguntas nos fóruns.

--Aaron Wroblewski

Gerente de programas, Windows