|
Software Development Kit to the Delphi-Win32 and Free Pascal compilers |
| Home > Wiki > br/MVP |
br/MVPenglish (en) | português (br) O Model-View-Presenter (MVP) framework fornecido, é baseado no padrão de design MVP (MVP design pattern). Referências para leitura adicional podem ser encontradas neste documento da Taligent e neste artigo do Martin Fowler.
[edit] Classes presenterQualquer formulário que apresente uma classe de negócio, necessita de um Presenter. O Presenter informa ao framework MVP que classe de negócio ele representa e fornece o mapeamento entre os controles e os atributos da classe. Exitem duas tarefas obrigatórias para configurar um Presenter:
[edit] Configurando os sub-presentersComo exemplo, um Presenter para a seguinte classe TClient: TClient = class(TPressObject) Name: String; City: Reference(TCity); TCity = class(TPressObject) Name: String; tem a seguinte sintaxe:
TClientPresenter = class(TPressMVPFormPresenter)
protected
procedure InitPresenter; override;
end;
...
procedure TClientPresenter.InitPresenter;
begin
CreateSubPresenter('Name', 'NameEdit');
CreateSubPresenter('City', 'CityComboBox', 'Name');
end;
Isto é tudo. CreateSubPresenter criará um Presenter para gerenciar a apresentação dos controles NameEdit e CityComboBox. Seu primeiro parâmetro é o nome do atributo, o segundo é o nome do controle contido no fomulário e o último, necessário apenas para atributos Estruturados (composições, cálculos e coleções), aponta para o atributo de uma outra classe que você queira apresentar no controle do formulário. Aqui é outro exemplo: TInvoice = class(TPressObject) IssueDate: Date; Client: Reference(TClient); Items: Parts(TInvoiceItem); Total: Currency; TInvoiceItem = class(TPressObject) Quantity: Integer; Product: Reference(TProduct); Total: Currency; TProduct = class(TPressObject) Name: String; A implementação do método TInvoicePresenter.InitPresenter para a classe TInvoice seria:
procedure TInvoicePresenter.InitPresenter;
begin
CreateSubPresenter('IssueDate', 'IssueDateEdit');
CreateSubPresenter('Client', 'ClientComboBox','Name');
CreateSubPresenter('Items', 'ItemsStringGrid','Quantity;Product.Name;Total');
CreateSubPresenter('Total', 'TotalEdit');
end;
O terceiro parâmetro descreve que atributos dos objetos Items devem ser apresentados no grid. Você também pode definir a largura (em pixels) e o título da coluna, neste parâmetro, veja a seguir: 'Quantity(10,"Qtde");Product.Name(200,"Nome Produto");Total(80)' [edit] Registrando presentersPresenters precisam ser registrados com o framework MVP. O registro fornece mapeamento entre os objetos de negócio, formulários e seus respectivos Presenters. Um exemplo: dentro da unit da classe MVP initialization TClientPresenter.RegisterBO(TClient, [fpExisting]); dentro da unit do formulário implementation uses ClientMVP, PressVCLBroker; // Delphi uses ClientMVP, PressLCLBroker; // Lazarus ... initialization PressVCLForm(TClientPresenter, TClientEditForm); // Delphi PressLCLForm(TClientPresenter, TClientEditForm); // Lazarus O primeiro parâmetro do método RegisterBO, indica a classe do objeto de negócio, o segundo é um set de opções indicando o propósito do Presenter. [fpNew] - todos presenters registrados com esta opção serão encontrados quando o framework precisar criar um formulário para um objeto novo. Por exemplo, esta opção seria usada para apresentar um formulário ao adicionar um novo item num grid de items de um pedido (invoice); [fpExisting] - todos presenter registrados com esta opção serão encontrados quando o framework precisar criar um formulário para edição de um objeto existente. Por exemplo, esta opção seria usada pra alterar um item já existente, dentro do pedido; [fpQuery] - relaciona presenters que mostram opções para preencher atributos com valores de outra classe em atributos References. Por exemplo: TJob = class(TPressObject) Name: String; PrintingColors: References(TColor); Quando o usuário precisar incluir mais itens no atributo PrintingColors, apresentado por um controle StringGrid, um presenter fpQuery será chamado. [fpNew, fpExisting] - o mesmo formulário será usado para objetos novos e existentes. Esta é a configuração padrão do parâmetro. [] - presenter nunca será encontrado pelo framework em ações disparadas pelo usuário. Útil quando o programador precisa mapear um formulário para uma classe de negócio e chamará o formulário manualmente. [edit] Presenter do formulário principalUm presenter para o formulário principal só é necessário se ele for associado a uma classe de negócio, ou o framework for usado para associar comandos com botões ou itens de menu. Criar uma classe Presenter principal (main) é simples como criar os outros Presenters: TMainPresenter = class(TPressMVPMainFormPresenter) protected procedure InitPresenter; override; end; ... procedure TMainPresenter.InitPresenter; begin BindCommand(TNewUserCommand, 'NewUserMenuItem'); end; O Presenter do formulário principal, não precisa ser registrado, no entanto, ele deve ser chamado no arquivo principal do projeto. Nota: Se não houver Presenter para o formulário principal, PressApp.Run deverá ser chamado no arquivo principal do projeto. Veja Criando uma aplicação para mais detalhes sobre a configuração de uma applicação PressObjects. [edit] Classes modelAs classes model controlam a apresentação do objeto de negócio e seus atributos: TClientModel = class(TPressMVPObjectModel) protected procedure InitCommands; override; end; ... procedure TClientModel.InitCommands; begin inherited; AddCommand(TClientReport); end; ... initialization TClientPresenter.RegisterFormPresenter( TClient, TClientForm, [fpNew, fpExisting], TClientModel); Este trecho de código (acima) é usado pra incluir um comando no menu de contexto (popup) do formulário de edição de clientes. Outro exemplo que cria um Model para um atributo:
TClientDueDateModel = class(TPressMVPDateModel)
protected
procedure InitCommands; override;
end;
...
procedure TClientDueDateModel.InitCommands;
begin
AddCommands([TClientDueDateCommand, nil]);
inherited;
end;
...
procedure TClientPresenter.InitPresenter;
begin
...
CreateSubPresenter('DueDate', 'DueDateEdit', '', TClientDueDateModel);
end;
Este exemplo, insere um comando como o primeiro item do menu de contexto do controle DueDateEdit. O parâmetro nil serve para incluir um separador, no menu, após o comando. [edit] Classes viewView são usadas pelo framework MVP para fornecer o comportamento e transferência de informação para componentes visuais. Por exemplo: TListViewView = class(TPressMVPItemsView) // métodos e membros usados pra gerenciar um controle TListView // <<veja o arquivo Source/Core/PressMVPView.pas para exemplos de implementação>> ... initialization TListViewView.RegisterView; Após o registro de uma View, o framework será capaz de usá-la toda vez que um formulário que gerencie a View, seja apresentado. É possível criar e registrar outra View que gerencie um controle já gerenciado pelo framework. Para tanto, é necessário herdar (inherit) diretamente da classe da view que já está registrada para gerenciar tal controle, então sobrepor (override) os métodos apropriados para modificar seu comportamento, como desejado. Não proceder desta forma resultará numa exceção, causada por ambiguidade. Como exemplo, se o seguinte for declarado: TMyOwnEditView = class(TPressMVPWinView) ... end; e registrado, uma exceção será levantada, pois TPressMVPEditView já está implementado e registrado pelo framework, para gerenciar um controle TEdit. Há duas Views gerenciando o mesmo controle, mas o framework não tem como saber qual usar. Mas se o seguinte for declarado: TMyOwnEditView = class(TPressMVPEditView) estará tudo bem, pois agora o framework verifica que a nova classe herda de TPressMVPEditView, e a usa em seu lugar. [edit] Classes commandCommands são amplamente usados para criar novas funcionalidades para formulários. TMarkClientAsGoodClientCommand = class(TPressMVPObjectCommand) protected function GetCaption: string; override; function GetShortCut: TShortCut; override; procedure InternalExecute; override; function InternalIsEnabled: Boolean; override; end; ... function TMarkClientAsGoodClientCommand.GetCaption: string; begin // útil apenas para criar comandos para modelos (menu de contexto) Result := 'Marcar este cliente como bom!'; end; function TMarkClientAsGoodClientCommand.GetShortCut: TShortCut; begin // útil apenas para criar comandos para modelos (menu de contexto) Result := VK_F9; end; procedure TMarkClientAsGoodClientCommand.InternalExecute; begin inherited; (Model.Subject as TClient).IsGood := True; end; function TMarkClientAsGoodClientCommand.InternalIsEnabled: Boolean; begin Result := not (Model.Subject as TClient).IsGood; end; A propriedade model, dentro do command, aponta para o Model a que pertence. A propriedade Subject, aponta para o objeto de negócio, ou um atributo, correspondente ao tipo do dono do command (um Model). Commands podem ser associados com itens de menu, controles "clicáveis" ou, no Model, ser apresentado como item no menu de contexto: procedure TClientPresenter.InitPresenter; begin ... BindCommand(TMarkClientAsGoodClientCommand, 'ClientIsGoodSpeedButton'); BindCommand(TMarkClientAsGoodClientCommand, 'ClientIsGoodMenuItem'); end; [edit] Classes interactorUm Interactor é usado pra modificar o comportamento de um Presenter:
TKeyboardInteractions = class(TPressMVPinteractor)
protected
procedure InitInteractor; override;
procedure Notify(AEvent: TPressEvent); override;
public
class function Apply(APresenter: TPressMVPPresenter): Boolean; override;
end;
...
class function TKeyboardInteractions.Apply(APresenter: TPressMVPPresenter):
Boolean;
begin
// O Interactor está interessado neste Presenter?
// sim, qualquer presenter
Result := True;
// ou depende, apenas se o presenter gerenciar uma view de CheckBox
Result := APresenter.View is TPressMVPCheckBoxView;
end;
procedure TKeyboardInteractions.InitInteractor;
begin
// o que o interactor precisa fazer logo depois que criado?
// vamos aguardar os eventos de teclado, vindos da view
Notifier.AddNotificationItem(Owner.View, [TPressMVPViewKeyDownEvent]);
end;
procedure TKeyboardInteractions.Notify(AEvent: TPressEvent);
begin
// o que o interactor precisa fazer quando a notificação for chamada?
// Nota: múltiplos eventos no segundo parâmetro do método
// AddNotificationItem, significam múltiplas classes AEvent
if AEvent is TPressMVPViewKeyDownEvent then
begin
TPressMVPViewKeyDownEvent(AEvent).Key; // tem o key code
TPressMVPViewKeyDownEvent(AEvent).Shift; // tem o shift state
end;
Owner; // aponta para o presenter
Owner.Model; // aponta para o model
Owner.Model.Subject; // aponta para o objeto de negócio (business object)
Owner.View; // aponta para a view
end;
...
initialization
TKeyboardInteractions.RegisterInteractor;
|
Personal tools |