![]() |
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Zitat:
Für Listen habe ich eine Observable Collection mit einem CollectionChanged Event. Dieser teilt mir genau mit, was sich in der Liste geändert hat. Damit kann ich dann jede andere Liste (Listendarstellung) mit möglichst wenig Stress (für mich und die Darstellung) synchron halten. Der Event liefert mir die Art der Änderung, den jeweiligen Index, wo sich etwas geändert hat und die jeweiligen Items (jeweils für OldItems und NewItems). |
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Einen anderen Gedanken den ich noch hätte wäre folgender (skizze siehe Screen) Anhang 42527 Im prinzip erstelle ich mir ein ScopeModel und immer wenn sich das ViewModel verändert (propertyChanged) rufe ich im Scope-Model die an diesen registrierten Methoden zur GUI (-Elemete) aktualisierung auf... eine Testbarkeit des ViewModels ist somit immer noch komplett unabhängig vom zustand des Views möglich! was haltet ihr davon? |
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Zitat:
Zitat:
|
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Aber du solltest den Scope mit dem ViewModel verbinden und nicht mit dem Model. Das ViewModel kapselt ja genau dieses Model und übersetzt, transferiert oder reichert das um weitere Informationen an.
Eine andere Möglichkeit ist das erstellen von WrapperKomponenten für die jeweiligen Controls. Dann hast du z.B. einen EditWrapper (nenn ihn meinetwegen Presenter) und der bekommt das ViewModel, den Namen der Eigenschaft, sowie einen Accessor (zum Lesen) und Mutuator (zum Schreiben). Klatsch ein Edit daran und schon tauscht das fröhlich aus. Genau sowas benutze ich um einen TreeView an ein Tree_ViewModel zu binden |
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Zitat:
Zitat:
welches Konzept ist für dich besser? |
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Liste der Anhänge anzeigen (Anzahl: 1)
Also hier erst mal das manuelle Binding in der View
Delphi-Quellcode:
unit View.MainView;
interface uses de.itnets.Events, de.itnets.References, MVVM.ViewModel.ViewModelBase, ViewModel.MainViewModel, System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, MVVM.View.FMX.Form.Base, FMX.Controls.Presentation, FMX.Edit; type TMainView = class( TViewBaseForm ) BarEdit: TEdit; { OnChangeTracking -> ControlChanged } FooEdit: TEdit; { OnChangeTracking -> ControlChanged } CheckLabel: TLabel; private FMain: WeakRef<TMainViewModel>; protected procedure AttachToViewModel( AViewModel: TViewModelBase ); override; procedure DetachFromViewModel( AViewModel: TViewModelBase ); override; procedure ViewModelPropertyChanged( Sender: TObject; const e: TPropertyChangedArgs ); override; public published procedure ControlChanged( Sender: TObject ); end; var MainView: TMainView; implementation {$R *.fmx} { TMainView } procedure TMainView.AttachToViewModel( AViewModel: TViewModelBase ); begin FMain := AViewModel as TMainViewModel; inherited; end; procedure TMainView.ControlChanged( Sender: TObject ); begin if FMain.IsAssigned then begin FMain.Reference.Bar := BarEdit.Text; FMain.Reference.Foo := FooEdit.Text; end; end; procedure TMainView.DetachFromViewModel( AViewModel: TViewModelBase ); begin inherited; FMain := nil; end; procedure TMainView.ViewModelPropertyChanged( Sender: TObject; const e: TPropertyChangedArgs ); begin inherited; if FMain.IsAssigned then begin if e.Match( 'Bar' ) then BarEdit.Text := FMain.Reference.Bar; if e.Match( 'Foo' ) then FooEdit.Text := FMain.Reference.Foo; if e.Matches( ['Bar', 'Foo'] ) then CheckLabel.Text := Format( 'Bar: "%s", Foo: "%s"', [FMain.Reference.Bar, FMain.Reference.Foo] ); end; end; end.
Delphi-Quellcode:
ist nur für die visuelle Rückmeldung, dass es tatsächlich im ViewModel angekommen ist.
CheckLabel
Im Anhang der Source (immer noch ohne mein Basis-Framework) und eine Exe zum Ausprobieren |
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
So ein Edit-Wrapper könnte ungefähr so aussehen
Delphi-Quellcode:
Denkbar ist allerdings auch ein Wrapper nach dieser Art
TEditView = class(Component)
public procedure SetViewModel( AViewModel : TViewModelBae; const PropertyName : string; const AAccessor : TFunc<string>; const AMutuaotr : TProc<string> ); propety Edit : TEdit; end;
Delphi-Quellcode:
Jetzt müsstest du allerdings per RTTI von dem PropertyNamen auf die Property zugreifen, was aber auch machbar ist. Das zusammen mit einem ValueConverter, der dafür sorgt, dass du auch von Boolean zu String und wieder zurück wechseln kannst macht die Sache richtig rund. Das geht dann schon in die Richtung LiveBinding
TEditView = class( TComponent )
public property Edit : TEdit; property PropertyName : string; property ViewModel : TViewModelBase; end; |
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Zitat:
Delphi-Quellcode:
oder übersehe ich da irgendwo was?
ViewModelPropertyChanged
|
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Zitat:
In Beitrag #2 habe ich am Ende die
Delphi-Quellcode:
und die
TViewModelBase
Delphi-Quellcode:
noch gezeigt, wo man das sehen kann.
TViewBaseForm
Bei der Verwendung dieser Pattern (egal ob MVC, MVP, MVVM, MVVP, ...) ist das A&O OOP und Vererben, bis der Arzt kommt. Dann wird vieles einfacher und schneller und die speziellen Teile sind nicht überfrachtet, weil ja der Basisteil schon im Vorfahr erledigt wird. Oder ich kann einfach erweitern, schon benutzen und trotzdem läuft noch alles (siehe die Erweiterung des Activity-ViewModels auf ProgressActivity-ViewModel). |
AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon
Zitat:
@Sir: eine Frage noch :) wie kann ich es erreichen das ich bei notify einen parameter mit übergeben kann? nach dem Motto
Delphi-Quellcode:
?
notify(para1);
Delphi-Quellcode:
procedure TStockpileModel.SetData(data: TArray<Double>);
begin self.data := data; // alle Ereignis-Behandlungs-Routinen der Liste aufrufen // wurde mit registerOn an das Model regestriert notify; end; BasisModel Als erstes erstes erstelle ich mir mal eine Basis Model, mit PropertyChanged-Handling
Delphi-Quellcode:
davon abgeleitete Klasse müssen bei Setter-Methoden jetzt die Funktion
TModel.notify
Delphi-Quellcode:
aufrufen
notify
Delphi-Quellcode:
Eigentliches Model
unit model;
interface type TEvent = procedure of object; TModel = class protected // interne Liste OnChange: array of TEvent; // Aufruf aller Routinen der Liste procedure notify; destructor destroy; override; public // neuer 'Event-Handler' in Liste procedure registerOn(routine: TEvent); // 'Event-Handler' aus Liste entfernen procedure registerOff(routine: TEvent); end; implementation // registriert neue routinen an den controller procedure TModel.registerOn(routine: TEvent); var n: integer; begin n := Length(OnChange); SetLength(OnChange, n + 1); OnChange[n] := routine; end; // de-registriert routinen vom controller procedure TModel.registerOff(routine: TEvent); var i, j: integer; begin i := Low(OnChange); while i <= High(OnChange) do // High liefert -1 bei leerem Array begin if @OnChange[i] = @routine // mit '@' nur Adressen vergleichen then begin for j := i to High(OnChange) - 1 do OnChange[j] := OnChange[j + 1]; SetLength(OnChange, Length(OnChange) - 1); end else i := i + 1; end; end; // alle Ereignis-Behandlungs-Routinen der Liste aufrufen destructor TModel.destroy; begin // inherited; end; procedure TModel.notify; var i: integer; begin for i := Low(OnChange) to High(OnChange) do OnChange[i]; end; end.
Delphi-Quellcode:
hier könnte die
TViewModel.SetBar(const Value: String);
Delphi-Quellcode:
Methoden-Aufruf weggelasen wird
notify;
Delphi-Quellcode:
Dann das ViewModel
unit model.stockpile;
interface uses model; type { Stockpile Model } TStockpileModel = class(TModel) private data: TArray<Double>; cdsBioLife: String; function GetBioLife: String; public function GetData: TArray<Double>; procedure SetData(data: TArray<Double>); property BioLife: String read GetBioLife; destructor destroy; override; end; implementation { TStockpileModel } destructor TStockpileModel.destroy; begin inherited; end; function TStockpileModel.GetBioLife: String; begin Result := cdsBioLife; end; function TStockpileModel.GetData: TArray<Double>; begin Result := self.data; end; procedure TStockpileModel.SetData(data: TArray<Double>); begin self.data := data; // alle Ereignis-Behandlungs-Routinen der Liste aufrufen // wurde mit registerOn an das Model regestriert notify; end; end.
Delphi-Quellcode:
Main
unit ViewModel;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, jpeg, StdCtrls, pngimage, JvPanel, JvExExtCtrls, JvExtComponent, Series, TeeShape, TeEngine, TeeProcs, Chart, Math, TeeGDIPlus, ComCtrls, JvExComCtrls, JvComCtrls, model, model.stockpile, controller, dOPCIntf, dOPCComn, dOPCDA, dOPC; implementation type TViewModel = class(TModel) private FModel: TStockpileModel; FBar: String; FFoo: string; function GetBioLifeCDS: String; function GetFishPictures: TBitmap; procedure SetBar(const Value: String); procedure SetFoo(const Value: string); public constructor Create; destructor Destroy; override; property BioLifeCDS: String read GetBioLifeCDS; property FishPictures: TBitmap read GetFishPictures; property Foo: string read FFoo write SetFoo; property Bar: String read FBar write SetBar; end; { TViewModel } constructor TViewModel.Create; begin inherited Create; FModel := TStockpileModel.Create; end; destructor TViewModel.Destroy; begin FModel.Free; inherited; end; procedure TViewModel.SetBar(const Value: String); begin if FBar <> Value then begin FBar := Value; notify; end; end; procedure TViewModel.SetFoo(const Value: string); begin if FFoo <> Value then begin FFoo := Value; notify; // OnPropertyChanged('Foo'); end; end; function TViewModel.GetBioLifeCDS: String; begin Result := FModel.BioLife; end; function TViewModel.GetFishPictures: TBitmap; begin try Result := TBitmap.Create; Result.LoadFromFile (ExpandFileName(IncludeTrailingPathDelimiter(ExtractFileDir(ParamStr(0)) + '') + '../../Assets/Images/sidebar-icon-error.jpg')); finally end; end; end.
Delphi-Quellcode:
....
// Model FViewModel: TStockpileModel; // Model wurde geändert procedure ViewModelPropertyChanged(Sender: TObject); implementation FViewModel := TViewModel.Create; // Wenn model geändert wird -> ViewModelPropertyChanged ausführen FViewModel.registerOn(ViewModelPropertyChanged); // Daten aus dem Model holen und GUI updaten procedure TForm1.ViewModelPropertyChanged(Sender: TObject); begin EditContentbarStartMarker.Text := FViewModel.Foo; end; // Daten ins Model zurückschreiben procedure TForm1.EditContentbarEndMarkerChange(Sender: TObject); begin FViewModel.Foo := TEdit(Sender).Text; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:48 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz