AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language MVC + Observer Pattern Konzept / Was haltet Ihr davon
Thema durchsuchen
Ansicht
Themen-Optionen

MVC + Observer Pattern Konzept / Was haltet Ihr davon

Ein Thema von FAM · begonnen am 5. Feb 2015 · letzter Beitrag vom 6. Feb 2015
Antwort Antwort
Seite 2 von 3     12 3      
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#11

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 09:28
Habe das ganze mit dem MVVP Konzept implementiert, das Problem ist jetzt allerdings meine Delphi Version (XE), dort gibt es noch keine Live-Bindings. Kennt jemand ein Workaround um das "zu simulieren"?
Ja, ich habe einmal an jedem ViewModel einen PropertyChanged Event hängen und kann damit entweder den BindSourceAdapter refreshen oder eben manuell den Wert übergeben. Wenn manuell gebunden, dann brauche ich aber auch ein manuelles Rückschreiben in das ViewModel, was aber eigentlich idR mit einem Event geht, wo dann von allen die Werte zurückgeschrieben werden.

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).
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo ( 6. Feb 2015 um 09:31 Uhr)
  Mit Zitat antworten Zitat
FAM

Registriert seit: 22. Dez 2014
47 Beiträge
 
Delphi XE Enterprise
 
#12

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 09:40
http://mitov.com/products/openwirelivebindings#overview
danke für den Hinweis.

Einen anderen Gedanken den ich noch hätte wäre folgender (skizze siehe Screen)

image1.jpg

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?
  Mit Zitat antworten Zitat
FAM

Registriert seit: 22. Dez 2014
47 Beiträge
 
Delphi XE Enterprise
 
#13

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 09:46
Ja, ich habe einmal an jedem ViewModel einen PropertyChanged Event hängen und kann damit entweder den BindSourceAdapter refreshen oder eben manuell den Wert übergeben.
das kommt dem schon nahe wie ich mir das denke.

Wenn manuell gebunden, dann brauche ich aber auch ein manuelles Rückschreiben in das ViewModel, was aber eigentlich idR mit einem Event geht, wo dann von allen die Werte zurückgeschrieben werden.
kannst du bitte mal einen source-snippet posten - wie du das genau implementierst (das rückschreiben)? Ich würde das (indirekt) über die write Methode in der jeweiligen Property vom eigentlichen Model machen, den aufruf dafür würde ich im view schreien

Geändert von FAM ( 6. Feb 2015 um 09:53 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#14

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 09:53
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
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
FAM

Registriert seit: 22. Dez 2014
47 Beiträge
 
Delphi XE Enterprise
 
#15

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 10:00
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.
Achso da ist in meiner Skizze ein fehler... der erste Kasten müsste das View enthalten, dann passt das denke ich

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
da bin ich jetzt raus - ich wüsste jetzt nicht wie ich für eine Visuelle Komponente einen Wrapper erstellen könnte? Code-Snippet ?!


welches Konzept ist für dich besser?

Geändert von FAM ( 6. Feb 2015 um 10:05 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#16

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 10:15
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.
CheckLabel ist nur für die visuelle Rückmeldung, dass es tatsächlich im ViewModel angekommen ist.
Im Anhang der Source (immer noch ohne mein Basis-Framework) und eine Exe zum Ausprobieren
Angehängte Dateien
Dateityp: zip ManualBinding.zip (1,98 MB, 10x aufgerufen)
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#17

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 10:26
So ein Edit-Wrapper könnte ungefähr so aussehen
Delphi-Quellcode:
TEditView = class(Component)
public
  procedure SetViewModel( AViewModel : TViewModelBae; const PropertyName : string; const AAccessor : TFunc<string>; const AMutuaotr : TProc<string> );
  propety Edit : TEdit;
end;
Denkbar ist allerdings auch ein Wrapper nach dieser Art
Delphi-Quellcode:
TEditView = class( TComponent )
public
  property Edit : TEdit;
  property PropertyName : string;
  property ViewModel : TViewModelBase;
end;
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
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
FAM

Registriert seit: 22. Dez 2014
47 Beiträge
 
Delphi XE Enterprise
 
#18

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 10:32
Also hier erst mal das manuelle Binding in der View
Delphi-Quellcode:
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.
ok cool, eine frage hätte ich da noch, sorgt dein Framework für den Aufruf der ViewModelPropertyChanged oder übersehe ich da irgendwo was?
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#19

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 10:42
ok cool, eine frage hätte ich da noch, sorgt dein Framework für den Aufruf der ViewModelPropertyChanged oder übersehe ich da irgendwo was?
Ja, und da das bei jeder View erfolgen muss und ich jede View von einer Basis-View ableite, erfolgt das Anhängen an den PropertyChanged Event auch eben in dieser Basis View -> weniger Arbeit für mich

In Beitrag #2 habe ich am Ende die TViewModelBase und die TViewBaseForm noch gezeigt, wo man das sehen kann.

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).
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo ( 6. Feb 2015 um 10:52 Uhr)
  Mit Zitat antworten Zitat
FAM

Registriert seit: 22. Dez 2014
47 Beiträge
 
Delphi XE Enterprise
 
#20

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 11:01
..., erfolgt das Anhängen an den PropertyChanged Event auch eben in dieser Basis View -> weniger Arbeit für mich
da ich jetzt nicht den luxus eines Frameworks habe mache ich das jetzt "per Hand"

@Sir: eine Frage noch
wie kann ich es erreichen das ich bei notify einen parameter mit übergeben kann?
nach dem Motto 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 TModel.notify davon abgeleitete Klasse müssen bei Setter-Methoden jetzt die Funktion notify aufrufen

Delphi-Quellcode:
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.
Eigentliches Model

TViewModel.SetBar(const Value: String); hier könnte die   notify; Methoden-Aufruf weggelasen wird

Delphi-Quellcode:
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.
Dann das ViewModel

Delphi-Quellcode:
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.
Main

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;

Geändert von FAM ( 6. Feb 2015 um 11:06 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:55 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz