There are many ways to implement communication between view and model when using MVP.
- Following the SupervisingController path, the model communicates directly to the view using bindings of any kind created in the presenter. DSharp comes to my mind, or Delphi XE2 LiveBindings. This is convenient, but it's also harder to UnitTest.
- Going down the PassiveView road, there are again several approaches to setup the links between model and view. Because PassiveView is more loosly coupled via interfaces to the view, it provides a better testing ground. I'll explain these in the following.
One way is to implement getters/setters for each EditControl's Value property (e.g. TEdit.Text, TDateTimePicker.Date) in the UI, getters only for readonly properties (think of Username of a Login screen: we will never set a value for this one) and setters for those that will only be written to (TLabel etc.). This way is using least ressources and easy to implement, but requires more coding.
You need to implement any feature by hand, e.g. disableling controls, marking required fields with a special color, validation and such become methods on your ViewInterface so the presenter could call them.
IMyView = interface
function GetName: string;
procedure SetName(value: string);
procedure MarkNameAsRequired;
function GetBirthdate: TDateTime;
procedure SetBirthDate(Value: TDateTime);
procedure MarkBirthDateAsInvalid; // Birthdate cannot be in the future or before 100 years
procedure SaveButtonEnabled(Value: boolean);
....
end;
This could be endlessly continued, but I'm sure you get the point. The UI designer is left to implement all this, and the presenter developer must rely on the designer to do his job well.
Another way is to use an adapter pattern, or rather a class implementing the adapter pattern. Such a class would be used as a mediator to communicate data and actions (thanks to Daniele Teti who
gave the idea). It would have some basic logic in itself.
IMyStringAdapter = interface
function GetValue: string;
procedure SetValue(value: string);
Procedure SetEnabled(value: boolean);
procedure IsRequired(value: boolean);
property Value: string read GetValue write SetValue;
....
end;
TMyStringAdapter = class(TInterfacedObject, IMyStringAdapter)
private
FProp: string;
function GetValue: string;
procedure SetValue(value: string);
public
constructor Create(Control: TObject; Prop: string);
Procedure SetEnabled(value: boolean);
procedure IsRequired(value: boolean);
property Value: string read GetValue write SetValue;
....
end;
The implementing view would then create once an instance of TMyStringAdapter with the control it should be bound to and a property being passed in the constructor.
NameAdapter:= TStringAdapter.Create(NameEdit, 'Text');
That's all! UI guys have nothing more to be concerned about, and the developer needn't to worry about anything that eventually isn't properly implemented.
Of course, one might argue I could create some specialized controls or at least adapterclasses for special controls. But the way I use it the UI designer can still decide which controls he wants to use to display a certain value. The presenter developer has full control over what's happening within the view, whatever it might look like.
And, as you may have noticed, model and view remain unaware of each other. exchanging one or the other require allmost zero changes in code.