Zum Event-Parameter:
Ich habe mir einen generischen MultiCast-Event geschrieben und davon einen speziellen PropertyChanged-Event erzeugt. Dieser Event hängt an jedem
ViewModel (im Model hat der nichts verloren
)
Siehe die Methode
OnPropertyChanged
Delphi-Quellcode:
unit MVVM.ViewModel.ViewModelBase;
interface
uses
de.itnets.Events,
de.itnets.References;
type
TViewModelBase =
class
private
FPropertyChanged: PropertyChangedEvent;
FDisplayName:
string;
function GetPropertyChanged: IPropertyChangedEvent;
protected
procedure SetDisplayName(
const Value:
string );
procedure OnPropertyChanged(
const PropertyName:
string = '
' );
overload;
procedure OnPropertyChanged(
const PropertyNames: TArray<
string> );
overload;
public
property DisplayName:
string read FDisplayName;
property PropertyChanged: IPropertyChangedEvent
read GetPropertyChanged;
end;
TViewModelClass =
class of TViewModelBase;
implementation
{ TViewModel }
function TViewModelBase.GetPropertyChanged: IPropertyChangedEvent;
begin
Result := FPropertyChanged;
end;
procedure TViewModelBase.OnPropertyChanged(
const PropertyNames: TArray<
string> );
var
LPropertyName:
string;
begin
for LPropertyName
in PropertyNames
do
begin
OnPropertyChanged( LPropertyName );
end;
end;
procedure TViewModelBase.OnPropertyChanged(
const PropertyName:
string );
begin
// Hier wird jetzt der Event durch die Gegend geschickt
FPropertyChanged.Invoke( Self, TPropertyChangedArgs.Create( PropertyName ) );
end;
procedure TViewModelBase.SetDisplayName(
const Value:
string );
begin
if FDisplayName <> Value
then
begin
FDisplayName := Value;
OnPropertyChanged( '
DisplayName' );
end;
end;
end.
Da diese Methode
protected
ist, kann ich die von allen abgeleiteten ViewModels aufrufen.
Warum kein Notify im Model?
Wenn sich im Model etwas ändert, dann ist das durch das
ViewModel passiert ... ja, äh dann soll bitte auch das ViewModel die Nachricht schicken.
Bei mir sind alle Models
immutable (unveränderlich). Wenn ich etwas ändere, dann erzeuge ich mir eine neue Model-Instanz mit den aktuellen Werten. Nach dem Speichern sende ich dann diese neue Instanz in die Runde und jeder der sich dafür interessiert, kann dann diese neue Instanz übernehmen.
Exakt zu diesem Zweck gibt es die
virtuelle Methode
TObject.Equals. Damit kann ich auf Gleichheit prüfen. Die Standard-Prüfung ist
Delphi-Quellcode:
function Object.Equals( Obj : TObject ) : Boolean;
begin
Result := Self = Obj;
end;
Das kann ich aber auch überschreiben
Delphi-Quellcode:
TPerson = class
private
FID : Integer;
FName : string;
public
constructor Create( const ID : Integer; const Name : string );
function SameEntityAs( Other : TPerson ): Boolean;
function Equals( Obj : TObject ) : Boolean; override;
property ID : Integer read FID;
property Name : string read FName;
end;
function TPerson.Equals( Obj : TObject ) : Boolean;
begin
Result := ( Self = Obj ) or Assigned( Obj ) and ( Self.ClassType = Obj.ClassType ) and SameEntityAs( Obj as TPerson );
end;
function TPerson.SameEntityAs( Other : TPerson ): Boolean;
begin
Result := (Self = Other ) or Assigned( Other ) and (Self.FID = Other.FID );
end;
Delphi-Quellcode:
procedure Test;
var
LPerson1, LPerson2 : TPerson;
begin
LPerson1 := TPerson.Create( 1, 'Foo' );
LPerson2 := TPerson.Create( 2, 'Foo' );
if LPerson1.Equals( LPerson2 ) then
WriteLn('Gleiche Person ist gemeint');
LPerson2 := TPerson.Create( 1, 'Bar' );
if LPerson1.Equals( LPerson2 ) then
WriteLn('Gleiche Person ist gemeint');
end;
Und noch etwas weitergedacht, kann ich sogar das ViewModel vergleichen und zwar mit einem anderen
TPersonViewModel
und einem
TPerson
:
Delphi-Quellcode:
TPersonViewModel = class
private
FPerson : TPerson;
public
function Equals( Obj : TObject ) : Boolean;
end;
function TPersonViewModel.Equals( Obj : TObject ) : Boolean;
begin
Result := (Self = Obj ) or Assigned( Obj ) and (
( ( Self.ClassType = Obj.ClassType ) and ( Self.FPerson.Equals( ( Obj as TPersonViewModel ).FPerson ) ) )
or
( Self.FPerson.Equals( Obj ) )
);
end;
Das auch noch auf die View übertragen und ich kann quasi alles mit jedem vergleichen.