Analisando a exceção ContractException

by Israel Aece 15. August 2010 21:17

A partir de agora, durante a escrita do nosso código, podemos recorrer ao Code Contracts, que é uma biblioteca que está sendo desenvolvida pela Microsoft, e que permite trabalhar com condições e garantir com que elas sejam atendidas durante a execução ou até mesmo de forma estática. Caso alguma das regras impostas seja violada, uma exceção será disparada, caracterizando um bug no sistema que consome este componente. O uso desse novo recurso tende a diminuir consideravelmente o uso do código if-then-throw.

A exceção que é disparada é do tipo ContractException e, propositalmente, ela foi criada como internal. Isso evita que se crie tratadores de erro que capture este tipo de exceção, e com isso burle o processo de verificação que é realizado por essa biblioteca. A aplicação que consome a classe deve se preocupar apenas em capturar exceções tradicionais (ArgumentNullException, IndexOutOfRangeException, etc.), pois enquanto houver exceções do tipo ContractException sendo disparadas, provavelmente o consumidor não está passando as informações da forma correta.

Há alguns momentos em que gostaríamos de mudar este comportamento, como por exemplo, eliminar essa validação durante a escrita de algum teste (isso envolve outra discussão, que é TDD com DbC). Para exemplificar, considere o código abaixo, que possui uma condição pré (Requires) e outra pós (Ensures), que recorre ao Code Contracts para validar e garantir que as informações são passadas da forma correta para o método Creditar e também garante o estado da classe ContaBancaria, após a execução deste mesmo método.

public class ContaBancaria
{
    public decimal Saldo { get; private set; }

    public ContaBancaria(decimal saldo)
    {
        this.Saldo = saldo;
    }

    public void Creditar(decimal quantia)
    {
        Contract.Requires(quantia > 0, "A quantia para deposito deve ser maior que 0");
        Contract.Ensures(Contract.OldValue(this.Saldo) + quantia == this.Saldo);

        this.Saldo += quantia;
    }
}

Se criarmos um código para consumir essa classe e, consequentemente, efetuar um crédito com valor negativo, teremos uma exceção do tipo ContractException. Para contornar esse comportamento, podemos recorrer ao evento estático ContractFailed da classe Contract. Esse evento define o argumento do tipo ContractFailedEventArgs, que expõe alguns membros interessantes, e alguns deles estão listados abaixo:

  • Condition: Uma string com a condição que não foi atentida.
  • FailureKind: Uma das opções exposta pelo enumerador ContractFailureKind, indicando se é uma condição pré, pós ou invariante.
  • Message: Uma string que representa a mensagem que descreve o problema.
  • OriginalException: Retorna a instância da exceção que causou o evento, que é utilizado quando você utiliza métodos que, por questões de compatibilidade, dispara exceções diferentes de ContractException.
  • SetHandled: Método que quando invoca, suprime o disparo da exceção que violou o contrato.
  • SetUnwind: Ao contrário do método anterior, quando invocado ele dispara a exceção ContractException depois que o tratador do evento ContractFailed é encerrado.

Com os membros que vimos acima, podemos agora ter acesso ao tipo do problema que ocorreu, e ainda conseguir burlar o problema de verificação que é realizado pelo Code Contracts. Abaixo temos um exemplo de como podemos proceder para fazer isso funcionar, ou seja, invocando o método SetHandled para que erros que violam o contrato não sejam mais entregues para o código consumidor:

static void Main(string[] args)
{
    Contract.ContractFailed += (sender, args) =>
    {
        Console.WriteLine("{0} - {1}", args.FailureKind, args.Condition);
        args.SetHandled();
    };

    new ContaBancaria(1000).Creditar(-100);
}

Tags:

C#

Omitindo tipos genéricos durante a construção

by Israel Aece 10. August 2010 22:38

Os Generics trouxeram um grande poder às linguagens .NET. O tipos genéricos podem ser utilizados por toda a classe onde ele está sendo criado e, consequentemente, podemos utilizar em seus construtores. O maior problema da criação de objetos que exigem um parâmetro genérico, é a necessidade de especificarmos esses tipos durante a sua construção, ou seja, o construtor não é capaz de inferir os tipos, o que torna o código um pouco ilegível. Para exemplificar, considere o seguinte código:

public class ProxyManager<TChannel, TBinding>
{
    private TChannel _channel;
    private TBinding _binding;

    public ProxyManager(TChannel channel, TBinding binding)
    {
        this._channel = channel;
        this._binding = binding;
    }

    //Outros Membros
}

Durante a sua criação, é necessário especificarmos os tipos genéricos para depois conseguir parametrizar o construtor com os valores efetivos. Se repararmos o código abaixo, note que não podemos omitir o que está entre os caracteres < e >, mesmo que construtores são uma espécie de método, eles não possibilitam a omissão dos mesmos, assim como acontece nos métodos genéricos.

var manager = 
    new ProxyManager<ServicoDeUsuariosClient, NetTcpBinding>(
        new ServicoDeUsuariosClient(), 
        new NetTcpBinding());

Para melhorar esse código e torná-lo mais simples de consumir a classe, podemos recorrer a criação de uma nova classe, que conterá um método estático que aceita exatamente os parâmetros genéricos exigidos pelo construtor da classe ProxyManager<TChannel, TBinding>, e o retorno será a própria classe criada, uma espécie de factory. O que acontece aqui é que todo o código burocrático acaba ficando dentro deste método.

public static class ProxyManager
{
    public static ProxyManager<TChannel, TBinding> Create<TChannel, TBinding>(TChannel channel, TBinding binding)
    {
        return new ProxyManager<TChannel, TBinding>(channel, binding);
    }
}

Com isso, a criação de um novo ProxyManager, se resume simplesmente ao código que vemos abaixo:

var manager = 
    new ProxyManager.Create(new ServicoDeUsuariosClient(), new NetTcpBinding());

Tags:

C#

DataBinding em WPF

by Israel Aece 22. January 2010 22:43

Já ouvimos falar muito sobre o termo DataBinding. Como sabemos, trata-se de um mecanismo para associar a informação de uma determinada origem à um determinado destino, criando uma dependência unidirecional ou bidirecional. Muitas vezes este termo está associado à alguma fonte de dados, que desejamos exibir suas respectivas informações na tela de uma aplicação qualquer.

Cada tecnologia implementa isso de uma forma diferente, com seus benefícios e possíveis limitações. O ASP.NET traz essa funcionalidade, onde você pode facilmente ligar uma fonte de dados, mas tem algumas limitações por conta de ser HTTP, que não mantém estado. Já o Windows Forms, fornece uma forma de databinding muito mais rico em termos de funcionalidades. A finalidade deste artigo é apresentar o databinding no WPF, exibindo suas funcionalidades, que facilitarão a forma com que lidamos com manipulações de UI.

A Microsoft incorporou no WPF uma forma muito mais evoluída para efetuar databinding, não se limitando apenas a vincular uma fonte de dados à controles, mas também permitindo que qualquer objeto preencha outro, incluindo controles. Isso quer dizer que poderemos definir o valor de uma propriedade de um controle com o valor de uma outra propriedade e de um outro controle. Tudo isso eliminando grande parte do código imperativo requerido pelo Windows Forms, pois a partir de agora, poderemos recorrer a código declarativo (XAML) para especificar essas "amarrações".

Grande parte da responsabilidade para fazer tudo isso funcionar, é a classe Binding, que está debaixo do namespace System.Windows.Data. Ela é responsável por manter o "canal de comunicação" entre a origem e o destino, e além disso, expõe uma série de propriedades que nos permite customizar o comportamento dessa comunicação. Entre as principais propriedades, temos:

  • ElementName: define o nome do elemento que servirá como fonte. Utilize esta propriedade quando desejar preencher uma outra propriedade com o valor de um controle do WPF.
  • Mode: determina a direção das informações.
  • NotifyOnSourceUpdated: valor boleano indicando se o evento SourceUpdated é disparado quando alguma atualização na fonte das informações ocorrer.
  • NotifyOnTargetUpdated: valor boleano indicando se o evento SourceUpdated é disparado quando alguma atualização no destino das informações ocorrer.
  • Path: espefica o nome da propriedade que será exibida.
  • RelativeSource: especifica uma fonte de forma relativa à posição do objeto atual.
  • Source: define o nome do objeto que servirá como fonte. Utilize esta propriedade quando desejar preencher com uma instância de um objeto.
  • XPath: a mesma finalidade da propriedade Path, mas define uma expressão XPath quando a fonte de informações for um arquivo Xml.

Note que nas propriedades acima, nós não temos uma propriedade que especifica qual propriedade no destino será carregada. Isso se deve, porque você aplicará a sintaxe de binding diretamente dentro da propriedade que você quer preencher. O exemplo abaixo mostra como podemos proceder para preencher um conteúdo de um controle Label com o texto de um Button:

<Button Name="button1" Content="Texto do Botão" />
<Label Name="label1" Content="{Binding ElementName=button1, Path=Content}" />

Alternativamente, você pode achar essa sintaxe um pouco ilegível, principalmente quando você tiver situações mais complexas. Se desejar, você pode recorrer à uma segunda forma de configurar o databinding, de forma hierárquica, onde você irá aninhar as configurações como um Xml tradicional, através de sub-elementos. O código abaixo ilustra esta segunda técnica:

<Button Name="button1" Content="Texto do Botão" />
<Label Name="label1">
    <Label.Content>
        <Binding ElementName="button1" Path="Content" />
    </Label.Content>
</Label>

Quando o modelo declarativa não é uma solução, pois você precisa dinamicamente determinar os databindings, você pode ainda utilizar o código C#/VB.NET para configurá-los. Tudo o que precisamos fazer é instanciar a classe Binding que falamos acima, e configurar as propriedades necessárias para que isso funcione, e que neste exemplo simples serão ElementName e Path. ElementName vai receber uma string com o nome do controle que servirá como origem, enquanto a propriedade Path, receberá a instância da classe PropertyPath, que em seu construtor você deverá especificar o nome da propriedade no objeto de origem, que quer que seja enviado para o destino.

Binding b = new Binding();
b.ElementName = "button1";
b.Path = new PropertyPath("Content");

this.label1.SetBinding(Label.ContentProperty, b);

Depois da instância da classe Binding configurada, utilizamos o método SetBinding do controle de destino, que no caso do exemplo é o Label. Além do Binding, esse método recebe a dependency property que receberá o valor da origem.

Carregando um Objeto

Acima vimos como podemos utilizar o databinding de uma forma diferente da qual estamos acostumado, que é através de controles de UI. Mas, o cenário mais comum é quando precisamos preencher um, ou vários controles de UI, com propriedades de um objeto. Eventualmente você tem um objeto que foi construído pela aplicação, e você precisa exibí-lo no formuário, distribuindo suas propriedades pelos controles do mesmo. O databinding também ajuda nisso, onde você pode especificar o tipo da classe, e o próprio WPF o instancia e, consequentemente, preenche os controles interessados, e tudo isso sendo feito declarativamente.

Ao invés de utilizar a propriedade ElementName, vamos agora recorrer à propriedade Source, que deve ser utilizada quando a origem se tratar de um objeto. Para exemplificar, vamos criar a instância da classe dentro dos resources do formulário, que nada mais é que uma coleção que pode armazenar qualquer tipo de objeto. Teremos uma classe simples chamada de Configuracao, contendo uma propriedade chamada Url. A instância desta classe será criada pelo WPF e estará armazenada estaticamente dentro dos recursos locais daquele formulário.

A sintaxe de binding agora consiste em configurar a propriedade Source com a instância criada e nomeada como "config". Continuamos a utilizar a propriedade Path, mas agora ela deverá refletir a propriedade do objeto que será preenchida pelo controle. Como podemos perceber, a propriedade Text do TextBox irá exibir a propriedade Url:

<Window x:Class="Teste.Window2"
    xmlns:local="clr-namespace:Teste">
    <Window.Resources>
        <local:Configuracao x:Key="config" />
    </Window.Resources>
    <Grid name="grid1">
        <TextBox Name="textBox1" Text="{Binding Source={StaticResource config}, Path=Url}" />
    </Grid>
</Window>

Suponhamos que temos vários controles e cada um receberá o valor de uma propriedade diferente. Ao invés de ficar repetindo a instância do objeto (config), podemos utilizar a propriedade DataContext. Essa propriedade nos permite compartilhar a mesma fonte por vários controles, e cada controle que o utilizará, apenas deve indicar qual propriedade ele estará vinculado. Repare que vinculamos o config à propriedade DataContext do controle Grid, e os controles inerentes à eles apenas mencionam qual propriedade cada um deles quer utilizar, sem a necessidade de especificar a propriedade Source.

<Window x:Class="Teste.Window2"
    xmlns:local="clr-namespace:Teste">
    <Window.Resources>
        <local:Configuracao x:Key="config" />
    </Window.Resources>
    <Grid name="grid1" DataContext="{StaticResource config}">
        <TextBox Name="textBox1" Text="{Binding Path=Url}" />
        <TextBox Name="textBox2" Text="{Binding Path=Timeout}"/>
    </Grid>
</Window>

Um detalhe importante aqui, é que se um controle não especificar nenhuma das propriedades de Binding (Source, RelativeSource ou Element), o WPF procura por algum elemento que possui a propriedade DataContext definida na árvore visual do formulário, e encontrando-o, tentará extrair o valor dele. Essa propriedade também pode ser configurada de via código, caso a instância do objeto precise de alguma manipulação adicional antes de ser usada pelo WPF.

this.grid1.DataContext = new Configuracao();

ObjectDataProvider

Como vimos acima, podemos criar a instância do objeto via código. Geralmente recorremos a essa técnica quando precisamos customizar a criação deste objeto. Mas o WPF fornece uma opção, que nos permite customizar de forma declarativa, algumas opções que podem ser utilizadas durante a criação deste objeto.

Entre essas opções, podemos definir alguns parâmetros para um construtor, fazer o databinding através de um método que retorna a instância do objeto, entre outras opções. Suponhamos que a partir de agora o construtor da classe Configuracao recebe como parâmetro uma string com a Url. Se tentar rodar a aplicação sem qualquer alteração, uma exceção será lançada dizendo que a classe não possui nenhum construtor público sem parâmetros. O ObjectDataProvider irá nos ajudar, permitindo especificar o valor deste parâmetro durante a criação:

<Window x:Class="Teste.Window2"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:Teste">
    <Window.Resources>
        <ObjectDataProvider x:Key="config" ObjectType="{x:Type local:Configuracao}">
            <ObjectDataProvider.ConstructorParameters>
                <system:String>http://wwww.israelaece.com</system:String>
            </ObjectDataProvider.ConstructorParameters>
        </ObjectDataProvider>
    </Window.Resources>
    <Grid DataContext="{StaticResource config}">
        <TextBox Name="textBox1" Text="{Binding Path=Url}" />
        <TextBox Name="textBox2" Text="{Binding Path=Timeout}"/>
    </Grid>
</Window>

RelativeSource

Já falamos das formas de databinding ElementName e Source, mas ainda nos resta uma terceira forma: a RelativeSource. Essa forma permite especificar como fonte, um objeto que está relativamente posicionado em relação ao objeto corrente. Essa opção traz três propriedades: Mode, AncestorType e AncestorLevel. A primeira delas recebe uma entre as seguintes opções:

  • FindAncestor: refere-se à um ancestral na árvore visual, que está acima em relação ao controle corrente. Essa opção exige que as propriedades AncestorType e AncestorLevel sejam definidas. A propriedade AncestorType define o tipo de elemento que será procurado, enquanto a propriedade AncestorLevel determina o nível de profundidade da busca.
  • PreviousData: permite referenciar o item anterior de uma coleção que está sendo exibida. Útil em templates.
  • Self: permite referenciar qualquer propriedade do objeto corrente.
  • TemplatedParent: permite referenciar um objeto que está envolvido em uma template.

O exemplo abaixo ilustra o uso do FindAncestor. Note que especificamos através do AncestorType o tipo de controle que desejamos procurar e o AncestorLevel, um número inteiro que determina até quantos níveis acima a busca será realizada. Neste caso, estamos interessados em exibir como o texto do botão o valor da propriedade Name de um Grid, e como estamos definindo o nível 2, ele irá apresentar o valor "g1" no botão.

<Window>
    <Grid Name="g1">
        <Grid Name="g2">
            <Button Name="button1">
                <Button.Content>
                    <Binding Path="Name">
                        <Binding.RelativeSource>
                            <RelativeSource Mode="FindAncestor" AncestorType="Grid" AncestorLevel="2" />
                        </Binding.RelativeSource>
                    </Binding>
                </Button.Content>
            </Button>
        </Grid>
    </Grid>
</Window>

Binding.Mode e Binding.UpdateSourceTrigger

Vimos até agora como podemos efetuar a ligação entre a origem e o destino das informações, mas não se resume a isso. Uma das característica do databinding permite também customizar que possíveis alterações sejam efetuadas para refletí-las tanto na origem quanto no destino. A propriedade Mode da classe Binding nos permite configurar como responder à essas ações, onde devemos escolher uma entre as cinco opções expostas pelo enumerador BindingMode:

  • Default: especifica que o binding utilizará o modo padrão estipulado pelo destino.
  • OneTime: especifica que o binding deve atualizar o destino quando a aplicação inicia ou quando os dados mudam, mas não deve atualizar o alvo quando subsequentes alterações são feitas na origem.
  • OneWay: especifica que o binding atualizará o destino quando a origem mudar. Alterações no destino não terão efeito na origem.
  • OneWayToSource: especifica que o binding atualizará a origem quando o destino mudar. Alterações na origem não terão efeito no destino.
  • TwoWay: especifica que as alterações feitas tanto na origem quanto no destino serão atualizadas automaticamente.

Outra propriedade que também é exposta pela classe Binding é a UpdateSourceTrigger, e que é utilizada quando a propriedade Mode é definida como OneWayToSource ou TwoWay. Essa propriedade determina como e quando a atualização das informações será realizada. Essa propriedade também receberá a informação oriunda de um enumerador, chamado UpdateSourceTrigger, onde as possíveis opções são:

  • Default: indica que a atualização será de acordo com o valor definido pela propriedade de destino, que muitas vezes é PropertyChanged. Propriedades que são editáveis pelo usuário, como a propriedade Text do TextBox, define o padrão como sendo LostFocus.
  • PropertyChanged: a fonte é atualizada quando a propriedade do destino é alterada.
  • LostFocus: a fonte é atualizada quando a propriedade de destino é alterada e quando o objeto perde o foco.
  • Explicit: a atualização da fonte será realizada quando você invocar explicitamente o método UpdateSource da classe Binding.

Coleções

Muitas vezes temos coleções de objetos que desejamos exibir através de controles, como por exemplo, ListBox. Da mesma forma que vimos anteriormente, para coleções, podemos proceder de forma semelhante, mas por se tratar de coleções, temos algumas novas propriedades que são exclusivas para esse cenário.

Controles que são considerados databound, expõe as seguintes propriedades DisplayMemberPath, ItemsSource e ItemTemplate. A primeira delas define o nome da propriedade do objeto que será exibida. Já a segunda representa a coleção que contém os itens que serão definidos com fonte das informações. Finalmente, a propriedade ItemTemplate, como o próprio nome diz, nos permite criar uma forma diferenciada para exibição de cada item da coleção. Dado uma coleção de clientes, onde cada elemento é representado por uma instância da classe Cliente, podemos fazer o seguinte:

<Window x:Class="Teste.Window3"
    xmlns:local="clr-namespace:Teste">
    <Window.Resources>
        <local:ColecaoDeClientes x:Key="cc" />
    </Window.Resources>
    <Grid>
        <ListBox
            Name="listBox1"
            ItemsSource="{Binding Source={StaticResource cc}}"
            DisplayMemberPath="Nome" />
    </Grid>
</Window>

O binding de coleções não está restrito à controles databound. Você pode também vincular uma coleção à um controle do tipo Label, mas como já era de se esperar, apenas o primeiro elemento será exibido. Para que se consiga navegar pelos elementos da coleção, você precisa recorrer à um mecanismo exposto pelo WPF que permite essa navegação.

Quando uma coleção é utilizada através do databinding, o WPF cria nos bastidores um objeto que implementa a interface ICollectionView (namespace System.ComponentModel). Essa interface disponibiliza membros que permite gerenciar a navegação, ordenação e agrupamento das informações. Para extrair este objeto, podemos utilizar o seguinte código:

ColecaoDeClientes cc = new ColecaoDeClientes();
ICollectionView view = CollectionViewSource.GetDefaultView(cc);

Entre os vários membros expostos por essa interface, temos: MoveCurrentToNext, MoveCurrentToPrevious, CurrentItem, etc. Não há o que comentar sobre cada um deles, pois são autoexplicativos. De posse da instância deste navegador, podemos navegar pelos registros de forma simples, sem precisar manualmente armazenar e incrementar ou decrementar índices na medida que o usuário for solicitando a visualização de um novo registro.

Data Templates

Muitas vezes, a visualização padrão fornecida por um controle databound não nos atende, ou por questões visuais ou porque a informação precisa ser customizada/formatada para cada item. Felizmente o WPF separa a funcionalidade do controle da sua visualização, permitindo que se customize completamente a aparência, sem perder ou ter que reescrever a funcionalidade de iteração de elementos.

Como o próprio nome diz, as data templates permite customizar a aparência de um controle, configurando como queremos que ele seja exibido. Tradicionalmente o ListBox exibe cada item um abaixo do outro, sem qualquer customização. Mas e se quisermos que cada elemento seja exibido como um TextBox, e dentro da propriedade Text termos a propriedade Nome vinculada? Abaixo podemos atingir esse objetivo com as data templates, onde customizamos o ListBox e para cada item, exibimos o valor da propriedade Nome dentro da propriedade Text de um TextBox. Repare que neste caso não utilizamos a propriedade DisplayMemberPath do ListBox, pois isso foi delegado ao template, para que ele determine onde e como mostrará o valor. Para quem já trabalhou com ASP.NET, mais precisamente com os controles DataList e Repeater, notará uma grande semelhança aqui.

<Window x:Class="Teste.Window3"
    xmlns:local="clr-namespace:Teste">
    <Window.Resources>
        <local:ColecaoDeClientes x:Key="cc" />
    </Window.Resources>
    <Grid>
        <ListBox Name="listBox1" ItemsSource="{Binding Source={StaticResource cc}}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Path=Nome}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

ADO.NET

Para preencher os controles do formulário com dados de um banco de dados, você pode recorrer as mesmas técnicas apresentadas aqui. DataSets possuem uma forma tranquila para uso em databinding, mesmo no WPF. Você pode utilizar a propriedade DataContext que vimos acima, definindo para ela a instância do DataSet/DataTable com os dados carregados de um banco de dados qualquer, e a partir daí, utiliza-se a mesma sintaxe para exibição dessas informações no formulário.

Já quando trabalhamos com tecnologias mais recentes, como o LINQ To SQL ou Entity Framework, eles sempre geram coleções, e estas estariam vinculadas aos controles que fossem exibí-las.

Conclusão: Este artigo demonstrou superficialmente todo o poder do databinding do WPF. Vimos como podemos utilizar as mais variadas formas de preencher um controle, não somente com dados de um banco de dados, mas também com informações que são geradas por outros controles ou até mesmo por outros objetos. Esse novo modelo de trabalho facilita bastante a forma como efetuamos a ligação das informações, algo que é um pouco mais complicado em seu principal concorrente, o Windows Forms.

Tags: ,

Data | WPF

Parse de Datas

by Israel Aece 8. January 2010 06:00

Para quem trabalha com o padrão de comunicação APL/APC, é muito comum que dentro de um arquivo as datas estejam formatadas da seguinte forma: ddMMyyyy (20/07/2010), ou seja, sem as barras ou qualquer outro tipo de separador entre o dia, mês e o ano.

Só que gostaríamos de trazer isso para um DateTime, para assim conseguir manipular melhor a data. Como fazer? Muitos optam pela manipulação da string, via método Substring, etc. Apesar de funcionar, dá um trabalho muito grande. Os métodos Parse da estrutura DateTime ou o ToDateTime da classe Convert, tentam converter a string seguindo o formato especificado pela cultura atual. A cultura geralmente tem uma formatação própria, como por exemplo, a cultura pt-BR é dd/MM/yyyy; já a en-US é MM/dd/yyyy, etc., e se utilizarmos uma dessas técnicas aqui, uma exceção do tipo FormatException é disparada, informando o problema.

Para resolver isso, você pode recorrer ao método ParseExact, também da classe DateTime, que também tem a finalidade de converter uma string em DateTime, mas seguindo uma formatação específica. Ao invocar este método, você deve informar o formato exato em que a data está definida dentro da string, e que no nosso caso é ddMMyyyy:

string data = "20072010";
DateTime dataDePagamento = DateTime.ParseExact(data, "ddMMyyyy", null);

Tags:

.NET Framework

Introdução aos Commands

by Israel Aece 6. January 2010 14:53

Entre as várias novidades que o WPF introduziu, uma das que mais vieram para nos ajudar são os Commands, que é semelhante ao popular design pattern Command do GoF. A Microsoft implementou este padrão no WPF para permitir uma melhor organização do código, já que ações são executadas quando o usuário manuseia algum controle ou quando alguma tecla (ou uma combinação delas) for pressionada.

Para ilustrar o problema que temos atualmente, vamos imaginar que temos um controle ListBox com o nome dos clientes e um Button que em seu evento Click, recupera o item selecionado, e exibe os detalhes cadastrais deste em um Label mais abaixo. Além disso, há um item na ToolBar da aplicação que tem a mesma funcionalidade, mas além, ainda possibilita que pressionando as teclas Ctrl + D, também deverá ter o mesmo efeito, ou seja, de mostrar os detalhes do cliente selecionado. Basicamente o código seria qualquer coisa próximo disso:

private void butto1_Click(object sender, EventArgs e)
{
    this.ExibirDetalhesDoCliente();
}

private void menuItem1_Click(object sender, EventArgs e)
{
    this.ExibirDetalhesDoCliente();
}

private void ExibirDetalhesDoCliente()
{
    if (this.Clientes.SelectedItem != null)
    {
        this.DetalhesDoCliente.Text = this.Clientes.SelectedItem.ToString();
    }
}

A imagem abaixo ilustra o formulário já em funcionamento:



Imagine também que gostaria de algo mais interativo, como por exemplo, fazer com que o botão que exibe os detalhes do cliente altere entre o estado de habilitado e desabilitado, de acordo com a seleção (ou não) de um item do ListBox. Isso faria com que eu espalhasse pelo código, verificações e checagens para garantir este resultado. Dependendo de como faz isso, pode resultar em um código ruim e propício a muitas redundâncias. Além disso, muitas vezes você tem várias outras ações, que são executadas por um formulário, e podem ser reutilizadas por vários controles dentro do mesmo, ou até mesmo em outros formulários dentro da mesma aplicação.

Como há muitas ações que são acessadas através de vários controles do formulário, pode também ter condicionais que estão ligados a elas, que permite a execução de acordo com alguma regra (como ter um item selecionado no ListBox). Outro ponto importante é a execução em batch, onde você pode elencar várias ações, adicionando cada uma delas na medida que vai clicando ou manipulando algum controle.

Essa funcionalidade fornecida pelo WPF tem a finalidade de conseguir controlar, de uma forma mais elegante e eficaz, esse tipo de situação, que é muito comum em aplicações Windows. A ideia é que esse padrão separa a execução da ação (chamada daqui para frente de comando) daquele que a invoca, ou seja, no evento Click por exemplo, você não terá mais o código, mas o comando será vinculado ao controle que o executará. Dessa forma, esta técnica permitirá que você vincule o mesmo comando à vários controles, mas a implementação somente acontecerá uma única vez.

O primeiro tipo que vamos analisar, é a interface ICommand. Essa interface fornece três membros autoexplicativos: Execute, CanExecute e CanExecuteChanged. O que chama mais a atenção é o método CanExecute, que retorna um valor boleano indicando se o comando poderá ou não ser executado (através do método Execute). No nosso exemplo, o comando deverá avaliar se o ListBox possui algum item selecionado, para que assim conseguimos visualizar os detalhes do mesmo.

Atualmente no WPF somente existe uma única classe que implementa esta interface, que é a RoutedCommand, e por tabela, a RoutedUICommand, que herda da RoutedCommand. Esse tipo de comando leva esse nome porque trabalha de forma semelhante aos routed events, ou seja, se o controle atual não for capaz de executar o comando, ele delega para o próximo elemento da árvore, até que alguém o execute. A única diferença entre as classes RoutedCommand e RoutedUICommand é que a segunda fornece uma propriedade chamada Text, que nada mais é do que a descrição do comando, que é usado para ser exibido por algum controle de UI.

Para começarmos a modificar o código acima, aquele para exibição de detalhes do cliente, vamos criar uma instância da classe RoutedUICommand, configurando em seu construtor o nome do comando e o tipo da classe onde ela é criada. Note que o modelo de criação segue mais ou menos a mesma forma de criação das dependency properties e dos routed events. Logo após a criação, note que estamos adicionando a possibilidade de utilizar uma combinação de teclas para executar o comando, através da coleção de gestures:

public static class MeusComandos
{
    public static readonly RoutedUICommand ExibirDetalhesDoCliente;

    static MeusComandos()
    {
        ExibirDetalhesDoCliente = 
            new RoutedUICommand("Exibir Cliente", "ExibirDetalhesDoCliente", typeof(MeusComandos));
        ExibirDetalhesDoCliente.InputGestures.Add(new KeyGesture(Key.D, ModifierKeys.Control));
    }
}

É importante dizer que o controle por si só não faz nada. Depois de decidir onde irá usá-lo, você precisa determinar o que ele irá fazer e se ele pode ou não executar esse comando. Para vincular o comando à algum controle, você precisará fazer uso dos CommandBindings. CommandBindings é uma espécie de listeners que aguardam um determinado comando ser executado, e utilizará os métodos já conhecidos (CanExecute e Execute) quando este comando for acionado. Repare no código abaixo, que em momento nenhum o código relacionado ao comando está vinculado ao controle; o que vinculamos no(s) controle(s) é o comando que ele deverá executar, através de um command binding.

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        this.ConfigurarComandos();
    }

    private void ConfigurarComandos()
    {
        this.CommandBindings.Add(
            new CommandBinding(
                MeusComandos.ExibirDetalhesDoCliente,
                cb_Executed,
                cb_CanExecute));

        this.button1.Command = MeusComandos.ExibirDetalhesDoCliente;
        this.menuItem1.Command = MeusComandos.ExibirDetalhesDoCliente;
    }

    private void cb_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        this.label1.Content =
            ((ListBoxItem)this.listBox1.SelectedItem).Content.ToString();
    }

    private void cb_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = this.listBox1.SelectedItem != null;
    }
}

A classe CommandBinding fornece vários eventos, e entre eles os eventos CanExecute e Executed, que é exatamente onde colocamos a regra para determinar se o comando pode ser executado, e o código referente ao comando propriamente dito, que no nosso caso, é mostrar os detalhes. Note que vinculamos a instância no comando criado (ExibirDetalhesDoCliente) na propriedade Command (exposta pela interface ICommandSource) do controle Button e do Menu, que por sua vez, recebe a instância de alguma classe que implemente a interface ICommand. Repare que não estamos nos vinculamos ao evento Click dos controles, pois internamente, depois deste evento (Click) disparado, o WPF avalia se a propriedade Command está definida com algum comando, e estando, o executará, extraindo dos command bindings quais são os eventos correspondentes ao comando. Como estamos lidando com eventos aqui, a propriedade CanExecute defina no parâmetro CanExecuteRoutedEventArgs, é que recebe essa resposta e encaminha para o WPF avaliar, e se estiver True, o executará. O código acima fica mais simples de ler, centraliza as regras e facilita a manutenção.

Algo interessante a se notar é que os controles que estão com o comando vinculado, monitoram o retorno do método/evento CanExecute, definindo a sua respectiva propriedade Enabled com o resultado deste método, ou seja, deixará o controle desabilitado até que ele retorna True. A imagem abaixo ilustra esse comportamento. Do lado esquerdo, a imagem está com os dois controles (Menu e Button) desabilitados até o momento que seleciono um dos itens do ListBox, que é a imagem da direita. Ao clicar no Menu, no Button ou pressionar Ctrl + D, o nome selecionado aparecerá no Label.



É interessante dizer também que os command bindings podem ser definidos de forma declarativa, através do XAML, eliminando assim todo o código que está definido dentro do método ConfigurarComandos.

<Window ... xmlns:local="clr-namespace:WpfApplication1">
    <Window.CommandBindings>
        <CommandBinding
            Command="local:MeusComandos.ExibirDetalhesDoCliente"
            CanExecute="b_CanExecute"
            Executed="b_Executed" />
    </Window.CommandBindings>

    <Grid>
        <Button Command="local:MeusComandos.ExibirDetalhesDoCliente" ... />
        <Menu Name="menu1">
            <MenuItem Header="Cadastro">
                <MenuItem
                    Name="menuItem1"
                    Command="local:MeusComandos.ExibirDetalhesDoCliente" />
            </MenuItem>
        </Menu>
    </Grid>
</Window>

Comandos Predefinidos

Há alguns comandos que já são bastante conhecidos, como é caso do Copiar, Colar, Recortar, etc., e a Microsoft mapeou eles e já embutiu no WPF, distribuindo-os em cinco categorias: ApplicationCommands, ComponentCommands, MediaCommands, NavigationCommands e EditingCommands. Cada categoria nada mais é do que uma classe estática e, consequentemente, todos os comandos também são declarados como estático, o que siginifica que haverá apenas uma única instância compartilhada de cada um com toda a aplicação.

Pelo fato dos controles serem estáticos (mesmo aquele que criamos acima), quem o "traz" para o controle atual é o CommandBinding, evitando conflitos entre outras regiões que fazem uso deste mesmo comando. Abaixo temos um exemplo simples de como podemos utilizar o comando Copy/Paste, expostos pela classe ApplicationCommands:

<Window ...>
    <Window.CommandBindings>
        <CommandBinding
            Command="ApplicationCommands.Copy"
            CanExecute="CommandBinding_CanExecute" />
    </Window.CommandBindings>

    <Grid>
        <TextBox Name="textBox1" />

        <Button Name="button1" Command="ApplicationCommands.Copy" />
        <Button Name="button2" Command="ApplicationCommands.Paste" />
    </Grid>
</Window>

E no C#, você precisa apenas testar se há algo digitado no TextBox e se ele está selecionado:

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = !string.IsNullOrEmpty(this.textBox1.SelectedText);
}

É interessante notar que em momento nenhum eu escrevi código para mandar o conteúdo selecionado para o ClipBoard ou para ler o conteúdo de lá. Esse código já está criado em outro lugar (no próprio sistema operacional), e o que o WPF faz é uma forma de interceptar e me permitir decidir de devo ou não continuar.

Conclusão: Eis mais uma grande funcionalidade que facilita muito a escrita de código no desenvolvimento de aplicações Windows. Além disso, torna o código mais fácil para dar manutenção, já que a centralização é um dos principais trunfos desta técnica.

Tags: , ,

WPF

Introdução aos Routed Events

by Israel Aece 5. January 2010 07:46

Assim como as Dependency Properties, uma outra grande inovação no WPF foram os Routed Events. As dependency properties adicionam às propriedades uma série de recursos adicionais, quando comparadas às propriedades tradicionais do .NET Framework, e o mesmo acontece com os routed events, fornecendo recursos bastantes interessantes, trazendo uma riqueza maior para os eventos, e que o WPF faz uso intenso disso.

Antes de efetivamente falar sobre os routed events, primeiro precisamos conhecer dois conceitos: logical e visual trees. Ambos tem a finalidade de descrever a interface que foi criada de forma declarativa ou imperativa, mas diferem na forma como visualizamos esses elementos. No primeiro caso, logical, cada elemento dentro desta árvore será visualizado de forma "macro", por exemplo, se tiver um controle do tipo Button, ele será apenas um Button com uma string que representa o seu texto; já o segundo conceito, visual, faz com que ao invés de visualizarmos o controle como um todo, ele "explode" o mesmo, permitindo o acesso a cada objeto que compõe a construção dele, e no caso do Button, teríamos o Button -> ButtonChrome -> ContentPresenter -> TextBlock.

Os conceitos que vimos acima é importante, pois tanto para dependency properties quanto para os routed events, eles utilizam a árvore lógica para a propagação de informações (dependency properties) e de ações (routed events). Como vimos acima, o Button é composto de vários outros sub-elementos, mas quando o usuário efetua o clique no botão, muito provavelmente ele poderá clicar em cima do TextBlock, e sendo assim, como o WPF traduzirá, ou melhor, entregará este evento para o evento Click do botão correspondente? Tudo isso graças ao recurso de propagação de eventos através da árvore visual.

A implementação de um routed event é bem semelhante a forma que criamos uma dependency property, ou seja, o evento é definido através de uma classe chamada RoutedEvent, declarada como estática e marcada como readonly no topo da classe onde ele será exposto. No construtor estático desta mesma classe, utilizamos o método estático RegisterRoutedEvent da classe EventManager, onde especificamos o nome do evento, a estratégia de roteamento, o tipo do event handler e o tipo da classe onde ele está sendo criado. Abaixo temos uma implementação de um evento em um controle:

public partial class MeuControle : UserControl
{
    public static readonly RoutedEvent ChangedStateEvent;

    static MeuControle()
    {
        ChangedStateEvent =
            EventManager.RegisterRoutedEvent(
                "ChangedState",
                RoutingStrategy.Tunnel, 
                typeof(RoutedEventHandler), 
                typeof(MeuControle));
    }

    public event RoutedEventHandler ChangedState
    {
        add { AddHandler(MeuControle.ChangedStateEvent, value); }
        remove { RemoveHandler(MeuControle.ChangedStateEvent, value); }
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        this.RaiseEvent(new RoutedEventArgs(MeuControle.ChangedStateEvent, this));
    }
}

O primeiro ponto importante a se notar, é a nomenclatura do evento. Por convenção, adotou-se sufixar com a palavra "Event", mas isso fica coerente quando estamos criando tudo em inglês; se o nome do evento for "EstadoAlterado", eu considero desnecessário este sufixo. Além disso, temos um evento tradicional do .NET, chamado de ChangedState. Isso serve apenas como um wrapper para o routed event que foi criado acima (lembre-se que as dependency properties também são expostas através de um wrapper), interceptando a adição e remoção do delegate que tratará o evento, recorrendo aos métodos AddHandler e RemoveHandler, respectivamente, que foram herdados da classe UIElement. E, finalmente, chega o momento de disparar o evento, e para isso utilizamos o método RaiseEvent, que dado os argumentos (falaremos mais abaixo sobre eles) correspondente ao delegate do evento, ele irá dispará-lo e os interessados poderão se anexar ao mesmo.

Os routed events nos permite ainda vincular à um evento não necessariamente em cima do próprio controle, mas através de qualquer controle dentro da hierarquia. Por exemplo, se tivermos um formulário WPF, e dentro dele um StackPanel, e dois botões dentro deste painel, o XAML correspondente seria:

<Window ...>
    <Grid>
        <StackPanel>
            <Button Name="btn1" Click="btn1_Click">Botao 1</Button>
            <Button Name="btn2" Click="btn2_Click">Botao 2</Button>
        </StackPanel>
    </Grid>
</Window>

A pergunta é: onde tratar o evento Click de cada botão? Se eu tratar o evento diretamente no botão (que é o mais comum), isso fará com que eu crie um event handler para cada botão dentro do StackPanel. Então porque não centralizar isso, onde independemente de qual botão for clicado, eu redirecionará sempre para o mesmo event handler? Poderia resolver isso vinculando o evento Click de todos os botões para o mesmo event handler, mas uma forma mais simples de fazer isso, é vinculando o evento ao controle que está acima na hierarquia do XAML, por exemplo:

<Window ...>
    <Grid>
        <StackPanel ButtonBase.Click="AlgumBotaoFoiClicado">
            <Button Name="btn1">Botao 1</Button>
            <Button Name="btn2">Botao 2</Button>
        </StackPanel>
    </Grid>
</Window>

Mas e se haverem mais do que um StackPanel dentro do meu formulário? Podemos então elevar o tratamento do evento para nível de formulário, como é mostrado abaixo:

<Window ... ButtonBase.Click="AlgumBotaoFoiClicado">
    <Grid>
        <StackPanel>
            <Button Name="btn1">Botao 1</Button>
            <Button Name="btn2">Botao 2</Button>
        </StackPanel>
        <StackPanel>
            <Button Name="btn3">Botao 3</Button>
            <Button Name="btn4">Botao 4</Button>
        </StackPanel>
    </Grid>
</Window>

O que determina como esses eventos serão disparados/propagados, é a estratégia de roteamento que você define quando cria o evento. Para isso, utilizamos o enumerador RoutingStrategy, que podemos escolher uma entre as três opções abaixo:

  • Tunnel: O evento é primeiramente disparado no controle raiz e depois em cima de todos os elementos até chegar no controle que efetivamente disparou o evento.
  • Bubble: O evento é primeiramente disparado no controle que efetivamente disparou o evento, subindo até a raiz.
  • Direct: O evento somente é disparado em cima do elemento de origem, assim como acontece com os eventos tradicionais do .NET.

Para utilizar o controle que criamos acima e consumir o evento, teremos um código semelhante a este:

<Window ...
    xmlns:app="clr-namespace:WpfApplication1"
    app:MeuControle.ChangedState="MeuControle_ChangedState">
    <Grid>
        <app:MeuControle ChangedState="MeuControle_ChangedState" />
    </Grid>
</Window>

private void MeuControle_ChangedState(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Sender: " + sender.ToString());
}

Se o evento for criado com a opção Tunnel, as mensagens serão: "Window1" e "MeuControle"; se definir a opção como Bubble, as mensagens serão "MeuControle" e "Window1" e, finalmente, se definir como Direct, somente aparecerá "MeuControle".

A assinatura dos event handlers dos routed events seguem, basicamente, o mesmo padrão de eventos tradicionais do .NET. O primeiro parâmetro (sender) é do tipo System.Object, que informa o objeto que disparou o evento. Já o segundo parâmetro (e) é uma classe do tipo RoutedEventArgs, que expõe quatro propriedades que trazem informações contextuais:

  • Source: System.Object que representa o elemento na árvore lógica que originalmente invocou o evento.
  • OriginalSource: System.Object que representa o elemento na árvore visual que originalmente disparou o evento.
  • Handled: Valor boleano que indica se o evento deverá prosseguir com a propagação para os elementos subsequentes da árvore. Se definí-lo como False, o próximo não será disparado.
  • RoutedEvent: A instância do routed event que foi disparado, que pode ser usado quando você direciona vários eventos para o mesmo event handler.

Conclusão: Assim como já acontece com as dependency properties, em um primeiro momento, esse tipo de evento torna o código um pouco mais complicado de ler, mas por outro lado, traz uma série de benefícios que você não consegue com os eventos tradicionais do .NET.

Tags: ,

WPF

MEF - Managed Extensibility Framework

by Israel Aece 17. December 2009 09:37
Revista Mundo .NET - 018 Toda aplicação que deseja suportar “plugins”, terá que se preocupar, também, em criar toda a infraestrutura necessária para suportá-los. Como essa preocupação está cada vez mais popular nos dias de hoje, a Microsoft trabalha em um projeto chamado MEF – Managed Extensibility Framework. Ao invés de explicitamente referenciar os componentes na aplicação, o MEF permitirá efetuar o descobrimento desses componentes de forma implícita, através de composição, gerenciando tudo o que for preciso para manter essas extensões, possibilitando que sua aplicação fique dependente de uma abstração, e não de uma implementação.

Atualmente o MEF encontra-se em desenvolvimento, e a Microsoft tem disponibilizado versões CTPs (Community Technology Preview), para avaliações do produto. A versão atual é a CTP 8, que já vem incorporada no Beta 2 do Visual Studio .NET 2010 e .NET Framework 4.0 e, provavelmente, fará parte da versão final do .NET Framework 4.0.

Este artigo tem a finalidade de abordar a API do MEF, mostrando todas as suas funcionalidades e como podemos utilizá-las em nossas aplicações.

Tags: , ,

.NET Framework

Introdução às Dependency Properties

by Israel Aece 16. December 2009 16:18

Quando desenvolvemos classes e/ou controles, geralmente expomos as suas características através de propriedades. Na maioria das vezes, essas propriedades encapsulam o acesso à membros privados internos, interceptando a escrita e leitura dos mesmos. Os controles ASP.NET geralmente não possuem esses membros internos, e armazenam o conteúdo diretamente no ViewState ou no ControlState da respectiva página.

As propriedades também são utilizadas por controles Windows Forms, seguindo a mesma estratégia das propriedades tradicionais que as linguagens disponibilizam. Mas aplicações Windows geralmente tem algumas características próprias, como validação e, principalmente, databinding, que é a possibilidade de vincular um objeto à algum controle de interface (UI). Há também algumas tarefas que são comuns em aplicações deste tipo, como por exemplo, a notificação da alteração de um valor, que fará eventuais mudanças nos demais controles.

Para tornar esse trabalho mais suave, a Microsoft introduziu, desde a primeira versão do WPF, as Dependency Properties. Esse tipo especial de propriedade é semelhante as propriedades das linguagens, mas com muito mais poder. A inteligência envolvida neste novo tipo de propriedade, permite trabalharmos com o código puramente declarativo (XAML), sem precisar de qualquer código procedural para efetuar alguma mudança na aparência quando algo acontecer.

Além disso, temos uma série de outros benefícios ao utilizá-las. O primeiro deles é a capacidade de sermos notificados quando o valor dessa propriedade for alterado, e o próprio sistema de Triggers do WPF faz uso desta funcionalidade, onde você consegue associar um valor à uma propriedade, e quando ela receber este valor, você poderá tomar alguma decisão, como alterar a cor de outro controle, disparo de outros eventos, etc. Há também outros pontos positivos que vamos analisar mais tarde, ainda neste artigo.

A sua utilização é relativamente simples, e não dispensa completamente o uso das propriedades tradicionais fornecidas pelas linguagens. As classes (ou controles) que querem fazer uso destas propriedades, deverão herdar direta ou indiretamente da classe DependencyObject, pois essa classe permitirá ao objeto participar do serviço de propriedades que o WPF disponibiliza. Além disso, para que uma propriedade seja registrada e, consequentemente, poder fazer uso de toda essa infraestrutura, cada propriedade exposta deverá manter um campo dentro da classe onde ela será declarada, definindo o tipo deste campo como sendo DependencyProperty. Abaixo temos a estrutura de uma dependency property:

public static readonly DependencyProperty TitleProperty =
    DependencyProperty.Register(
        "Title", 
        typeof(string), 
        typeof(MeuControle));

public string Title
{
    get
    {
        return this.GetValue(TitleProperty).ToString();
    }
    set
    {
        this.SetValue(TitleProperty, value);
    }
}

O primeiro ponto que vemos é que a classe DependencyProperty não é instanciada diretamente. Para registrá-la, você precisa invocar o método estático Register (dessa mesma classe), que em sua versão mais básica, receberá o nome da propriedade, o tipo da propriedade e o tipo onde essa propriedade está sendo criada. Outro ponto bastante curioso é que esse campo é declarado como estático. Mas e se houverem várias instâncias dessa classe, o valor da propriedade será compartilhado? Não. Na verdade, as propriedades são salvas em uma espécie de dicionário, onde a chave deverá ser única dentro do tipo. Aqui é a grande diferença, ou seja, ao invés de termos um campo privado para cada propriedade exposta, que muitas vezes ficavam com o seu valor padrão, as dependency properties resolvem este problema armazenando somente os valores que são modificados pela instância, enquanto as outras compartilham um - mesmo - valor padrão, reutilizando o valor padrão por todas as instâncias.

A seguir temos a propriedade Title, que por sua vez, serve apenas como um wrapper para o objeto que criamos acima, expondo o tipo efetivo da propriedade, que no caso acima é string. Note que em momento nenhum você acessa o objeto TitleProperty diretamente; toda a manipulação é realizada pelos métodos GetValue e SetValue, para ler e escrever, respectivamente. Como vimos, esses métodos estão disponíveis para todas as classes que derivam da classe DependencyObject, que realiza todas as etapas necessárias para determinar se o valor já foi sobrescrito pela instância corrente, e caso tenha sido, este será retornado ao invés de seu valor padrão.

Ainda há a possibilidade de registrar uma dependency property como somente leitura, pois pode haver propriedades que apenas reportam o estado interno da classe, como por exemplo, a propriedade IsMouseOver da classe Control, que retorna um valor boleano indicando se o ponteiro está ou não posicionado em cima daquele controle. Neste caso, o valor é definido internamente pelo WPF.

A classe DependencyProperty fornece para essa finalidade, um método também estático, chamado RegisterReadOnly, que depois de registrado, retornará uma instância da classe DependencyPropertyKey, representando a chave para a propriedade recém registrada.

private static readonly DependencyPropertyKey TitlePropertyKey =
      DependencyProperty.RegisterReadOnly(
          "Title", 
          typeof(string),
          typeof(MeuControle));
 
public static readonly DependencyProperty TitleProperty =
      TitlePropertyKey.DependencyProperty;

public string Title
{
    get
    {
        return this.GetValue(TitleProperty).ToString();
    }
    private set
    {
        this.SetValue(TitlePropertyKey, value);
    }
}

Neste caso, veja que a chave é declarada de forma privada, para que somente o interior da classe onde ela é declarada tenha acesso. A criação do objeto DependencyProperty ainda é necessário, mas não será através do método Register. A classe DependencyPropertyKey define uma propriedade chamada DependencyProperty, que retorna a propriedade para acessá-la através do método GetValue, seguindo o mesmo esquema que vimos acima. Na propriedade Title, a escrita também está protegida pelo modificador private, e note que a alteração está sendo feita, passando o chave e não a instância da DependencyProperty como fizemos no primeiro exemplo.

Metadados

Como vimos acima, a classe DependencyProperty ainda fornece outras versões (overloads) do método Register. O que veremos a seguir, é aquele que recebe os metadados. Através destes metadados, poderemos configurar uma série de características destas propriedades, como por exemplo: valor padrão, notificação quando o valor for alterado, coerção, etc. A principal classe que podemos utilizar para definição destes metadados, é a FrameworkPropertyMetadata.

Entre as principais funcionalidades que esta classe fornece, temos a possibilidade de informar o valor padrão da propriedade, e enquanto você não definir um novo valor para uma propriedade, será este valor que será retornado. Além disso, podemos definir através de delegates, ações que desejamos executar quando o valor for alterado ou alguma ação para efetuar uma coerção no valor. Abaixo temos o exemplo de como utilizar esses metadados:

public static readonly DependencyProperty TitleProperty =
    DependencyProperty.Register(
        "Title",
        typeof(string),
        typeof(MeuControle),
        new FrameworkPropertyMetadata(
            "Título Indefinido",
            (o, e) => MessageBox.Show(e.NewValue.ToString()),
            (d, e) => string.Format("*** {0} ***", e)));

O primeiro parâmetro informado no construtor da classe FrameworkPropertyMetadata, é o valor padrão da propriedade. Esse parâmetro é do tipo System.Object, pois a propriedade poderá ser de qualquer tipo. Todas as instâncias desta classe sempre compartilharão este mesmo valor, a menos que uma instância específica o sobrescreva, e a partir daí, esta instância terá o seu próprio valor.

Os dois parâmetros seguintes são delegates, onde o primeiro deles (PropertyChangedCallback) representa o método que será disparado quando o valor desta propriedade for alterado. O argumento "e" é do tipo DependencyPropertyChangedEventArgs, e entre suas propriedades, temos OldValue e NewValue, que são autoexplicativas. Já o terceiro parâmetro (CoerceValueCallback), define um método de coerção, que tem a finalidade de customizar/formatar a entrada da informação de acordo com alguma regra, que acontecerá antes de armazenar o valor definitivamente. O "e" que vemos sendo passado para este método, traz o valor atual; a assinatura deste delegate também retorna um System.Object, que é justamente o valor depois de alterado.

Validação

Outro ponto importante quando falamos sobre propriedades, é a questão de validação. As validações geralmente são avaliadas antes do valor ser efetivamente armazenado. As dependency properties também já trazem a validação nativamente, que podemos configurar e vincular durante a criação de uma propriedade.

Para isso, o método Register ainda fornece em um de seus overloads, um parâmetro do tipo ValidateValueCallback, que também é um delegate e que recebe o valor que está sendo passado para a propriedade e retorna um valor boleano, indicando se o parâmetro está válido ou não. O seu uso está sendo exibido abaixo, e note que antes da propriedade receber o valor, a consistência para verificar se ele está correta será avaliada, e caso retorne False, uma exceção será disparada.

public static readonly DependencyProperty TitleProperty =
    DependencyProperty.Register(
        "Title",
        typeof(string),
        typeof(MeuControle),
        new FrameworkPropertyMetadata(
            "Sem Título Definido",
            (o, e) => MessageBox.Show(e.NewValue.ToString()),
            (d, e) => string.Format("*** {0} ***", e)),
        p => p != null && p.ToString().Length > 0);

Conclusão: Apesar de no primeiro momento tornar o código um pouco ilegível, percebermos no decorrer deste artigo o poder deste tipo de propriedade. Grande parte das tarefas de manipulação de UI, e que antes ficavam espalhadas pelo código, agora podemos centralizar, tornando a manutenção e reutilização muito mais simples. Isso sem contar que utilizar esse tipo de propriedade habilita grande parte dos recursos exclusivos do WPF, como estilos, databinding, etc.

Tags: ,

WPF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host