sexta-feira, 9 de setembro de 2011

LiveBindings no delphi XE2

Hoje vamos falar sobre LiveBindings, um dos novos recursos do Delphi XE2.

LiveBindings é um mecanismo de data-binding suportados pela VCL e FireMonkey. Ele é baseado em expressões (binding expressions), o que significa que usa expressões para ligar objetos uns aos outros, por meio de suas propriedades.

É importante salientar que o LiveBinding foi criado devido a inclusão do FireMonkey, uma vez que o mesmo não possui componentes DB-Aware. Ao invés da criação desses componentes optou-se por criar um mecanismo de data-binding, e foi uma decisão muito feliz. No caso do FireMonkey os componentes podem ser ligados ao DataSource de maneira relativamente simples e de forma visual.



Minha maior expectativa era pode utilizar esse recursos para ligar objetos de negócio a componentes visuais não data aware, deixando a manipulação dos dados mais transparente, sem precisar ficar alimentando manualmente os componentes visuais e depois recuperar esse valores também manualmente, e confesso que fiquei decepcionado com as opções oferecidas nesse sentido. É possível, mais é muito mais verboso e intrusivo do que eu gostaria que fosse.

No site da embarcadero há três tutoriais: Tutorial: Using LiveBinding Programatically, Tutorial: Using LiveBinding in VCL Applications e Tutorial: Using LiveBinding to Create a FireMonkey Application Without Code.

De maneira resumida, quando às ligações envolvem apenas componentes visuais, ela é feita através do object inspector. Os componentes visuais possuem uma propriedade chamada LiveBindings, onde você deve clicar e selecionar a opção New LiveBinding.... Isso criará um BindExpression, onde você deve configurar:
- ControlExpression: Propriedade do componente selecionado que deverá ser atualizada.
- SourceComponent: Componente de origem. Quando ele for modificado o componente atual será notificado.
- SourceExpression: Propriedade que será lida para atualizar a propriedade ControlExpression do componente atual.

Esse TBindExpression fica armazenado em um componente TBindingsList, que é adicionado ao form automaticamente.

Relativamente simples. Só tem um porém, dessa forma ainda não funcionará, porque não foi incluído recurso de auto notificação, ou seja, a VCL não gerencia os objetos envolvidos para fazer a notificação automaticamente quando o valor da propriedade do SourceComponent for modificada. Temos que fazer isso manualmente.Em cada objeto envolvido você tem que implementar o método OnChange e invocar BindingsList.Notify(ObjetoAlterado, 'PropriedadeAlterada'), para que as alterações sejam propagadas para os "ouvintes" do objeto.

Agora vamos ver como fazer o binding programaticamente, utilizando objetos não visuais. Dado uma classe TMyObject, vamos fazer o binding da classe com um TEdit adicionado em um form.

type
  TMyObject = class(TObject)
  private
    FStringValue: String;
    procedure SetStringValue(const Value: String);
  public
    property StringValue: String read FStringValue write SetStringValue;
  end;

Para declarar o binding precisamos da unit System.Bindings.Helper, e código fica assim:
TBindings.CreateManagedBinding(
        { inputs }
        [TBindings.CreateAssociationScope([Associate(FMyObject, 'MyObject')])],
        'MyObject.StringValue',
        { outputs }
        [TBindings.CreateAssociationScope([Associate(Edit1, 'edit1')])],
        'edit1.text',
        nil);

Feito isso, quando alterar o valor da propriedade StringValue do objeto MyObject, o valor do edit1.text será alterado automaticamente, certo? Errado! Assim como o caso anterior, a notificação também não é automática. Quando o valor da propriedade for alterado, também devemos notificar o gerenciador.

procedure TMyObject.SetStringValue(const Value: String);
begin
  if (FStringValue <> Value) then
  begin
    FStringValue := Value;
    TBindings.Notify(FMyObject, 'StringValue');
  end;
end;

O último detalhe é que os gerenciadores de bindings nos dois casos apresentados não são os mesmos. O TBindingsList gerencia uma lista de TBindExpression, enquanto, o
TBindings gerencia uma lista de TBindingExpression. Desse modo, se por algum motivo um componentes está associado aos dois tipos de binding, os dois gerenciadores devem ser notificados.

Eu sinceramente esperava mais do que isso. Acredito ainda não ser possível utilizar isso em produção - utilizando VCL -, mais não posso negar que já é um avanço. Espero que na próxima versão esse recurso evolua e torne esse trabalho mais simples e transparente, com notificação automática e um gerenciador único para controlar o binding.

Até a próxima.

4 comentários:

  1. E ai Fabricio!

    Livebindings poderia ser usado para desenvolver os componentes da camada de persistência ? Ligando um TObject aos componentes visuais de banco ? É isso ?

    Att,

    Sakamoto

    MyTraceLog - Registro de um DBA
    http://mytracelog.blogspot.com

    ResponderExcluir
  2. Isso mesmo. O delphi incorporou uma biblioteca nova chamada FireMonkey pra desenvolver pra OS X, e como essa biblioteca não tem componentes DB Aware, implementaram esse mecanismo de binding. 
    Ficou legal mais precisa amadurecer mais pra realmente viabilizar um framework em cima disso.

    Abraços

    ResponderExcluir
  3. Confesso que o LiveBindings me decepcionou um pouco. Na aplicação de exemplo da própria Embarcadero, além de vc "declarar" todas as ligações entre as partes, ainda precisa notificá-las sobre os dados. Eu estava esperando algo como no WPF, onde vc estabelece o Binding, e depois apenas informa o Mode da ligação e está pronto. Mas acredito que isso vá melhorar nas próximas versões.

    ResponderExcluir
  4. Uma alternativa ao LiveBindings é um componente Dataset que pode ser populado com objetos nativos do Delphi, encontrado em http://www.inovativa.com.br/public

    ResponderExcluir