Einzelnen Beitrag anzeigen

Benutzerbild von Sir Rufo
Sir Rufo

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

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

  Alt 5. Feb 2015, 14:23
Richtig spannend wird es, wenn man das erweitern möchte:

z.B. benötigen wir auch eine Aktivitätsanzeige mit einem Fortschritt. Nichts leichter als das
Delphi-Quellcode:
unit ViewModel.ActivityViewModel;

interface

uses
  MVVM.ViewModel.ViewModelBase;

type
  TActivityViewModel = class( TViewModelBase )
  private
    FInfo: string;
    procedure SetInfo( const Value: string );
  public
    property Info: string read FInfo write SetInfo;
  end;

  TProgressActivityViewModel = class( TActivityViewModel )
  private
    FProgress: Single;
    procedure SetProgress( const Value: Single );
  public
    property Progress: Single read FProgress write SetProgress;
  end;

implementation

{ TActivityViewModel }

procedure TActivityViewModel.SetInfo( const Value: string );
begin
  if FInfo <> Value
  then
    begin
      FInfo := Value;
      OnPropertyChanged( 'Info' );
    end;
end;

{ TProgressActivityViewModel }

procedure TProgressActivityViewModel.SetProgress( const Value: Single );
begin
  if FProgress <> Value
  then
    begin
      FProgress := Value;
      OnPropertyChanged( 'Progress' );
    end;
end;

end.
Schon haben wir eine Aktivität mit Fortschritt.

Dann mal in das MainViewModel und die Aktivitäten eingebaut (nur die Änderungen)
Delphi-Quellcode:
unit ViewModel.MainViewModel;

interface

uses
  System.SysUtils,
  System.Classes,
  System.Threading,

  de.itnets.Commands,
  de.itnets.References,

  MVVM.ViewModel.ViewModelBase,
  ViewModel.WorkspaceViewModel,
  ViewModel.ActivityViewModel;

type
  TMainViewModel = class( TWorkspaceViewModel )
  private
    FSomeProgressActionCommand: ICommand;
    FSomeRandomActionCommand: ICommand;
    function GetSomeProgressActionCommand: ICommand;
    function GetSomeRandomActionCommand: ICommand;
  public
    property SomeProgressActionCommand: ICommand read GetSomeProgressActionCommand;
    property SomeRandomActionCommand: ICommand read GetSomeRandomActionCommand;
 end;

implementation

{ TMainViewModel }

function TMainViewModel.GetSomeProgressActionCommand: ICommand;
begin
  if not Assigned( FSomeProgressActionCommand )
  then
    FSomeProgressActionCommand := TRelayCommand.Create(
      procedure
      var
        LActivity: AutoRef<TProgressActivityViewModel>;
      begin

        SetCanClose( False );

        // Aktivitätsanzeige setzen

        LActivity := TProgressActivityViewModel.Create;
        LActivity.Reference.Info := 'Performing SomeProgressAction';

        SetActivity( LActivity );

        // Task starten

        TTask.Run(
            procedure
          var
            LIdx: Integer;
          begin
            LActivity.Reference.Progress := 0;
            for LIdx := 1 to 10 do
              begin
                Sleep( 200 );
                LActivity.Reference.Progress := LIdx * 10;
              end;

            // Aktivitätsanzeige ausschalten
            TThread.Synchronize( nil,
                procedure
              begin
                SetActivity( nil );
                SetCanClose( True );
              end );
          end );
      end,
      function: Boolean
      begin
        Result := not FActivity.IsAssigned;
      end );
  Result := FSomeProgressActionCommand;
end;

function TMainViewModel.GetSomeRandomActionCommand: ICommand;
begin
  if not Assigned( FSomeRandomActionCommand )
  then
    FSomeRandomActionCommand := TRelayCommand.Create(
      procedure
      var
        LActivity: AutoRef<TActivityViewModel>;
        LProgressActivity: AutoRef<TProgressActivityViewModel>;
      begin

        SetCanClose( False );

        // Aktivitätsanzeige setzen

        LActivity := TActivityViewModel.Create;
        LProgressActivity := TProgressActivityViewModel.Create;

        SetActivity( LActivity );

        // Task starten

        TTask.Run(
            procedure
          var
            LIdx: Integer;
          begin
            TThread.Queue( nil,
                procedure
              begin
                LActivity.Reference.Info := 'Init data...';
                SetActivity( LActivity );
              end );

            Sleep( 1000 );

            TThread.Queue( nil,
              procedure
              begin
                LProgressActivity.Reference.Info := 'Reading data...';
                LProgressActivity.Reference.Progress := 0;
                SetActivity( LProgressActivity );
              end );
            for LIdx := 1 to 20 do
              begin
                Sleep( 150 );
                TThread.Queue( nil,
                  procedure
                  begin
                    LProgressActivity.Reference.Progress := LIdx * 5;
                  end );
              end;

            TThread.Queue( nil,
                procedure
              begin
                LActivity.Reference.Info := 'Cleanup system...';
                SetActivity( LActivity );
              end );

            Sleep( 1000 );

            // Aktivitätsanzeige ausschalten
            TThread.Synchronize( nil,
              procedure
              begin
                SetActivity( nil );
                SetCanClose( True );
              end );
          end );
      end,
      function: Boolean
      begin
        Result := not FActivity.IsAssigned;
      end );
  Result := FSomeRandomActionCommand;
end;

end.
Da wir TProgressActivityViewModel von TActivityViewModel abgleitet haben brauchen wir die View nicht ändern um lauffähig zu bleiben. Nur der Progress wird eben nicht angezeigt, aber die Funktionalität bleibt gewahrt.

Ok, dann bauen wir uns eine entsprechende View (Frame mit einer ProgressBar und einem TextFeld) und bringen das auf die MainView. Damit das dann auch genutzt wird benötigen wir diese Änderungen an der View (nur die geänderten Teile)
Delphi-Quellcode:
unit View.Form.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,
  View.Form.WorkspaceView, MVVM.View.FMX.Frame.Base,
  FMX.Objects, System.Actions, FMX.ActnList, FMX.Layouts,
  
  View.Frame.AcitivityView,
  View.Frame.ProgressAcitivityView;

type
  TMainView = class( TWorkspaceView )
    ActivityCurtain: TRectangle;
    ActivityView1: TActivityView;
    ToolBar1: TToolBar;
    SpeedButton1: TSpeedButton;
    ActionList1: TActionList;
    SomeActionAction: TAction;
    CloseAction: TAction;
    SpeedButton2: TSpeedButton;
    SomeProgressActionAction: TAction;
    SpeedButton3: TSpeedButton;
    ProgressActivityView1: TProgressActivityView;
    SomeRandomActionAction: TAction;
    SpeedButton4: TSpeedButton;
    procedure SomeActionActionExecute( Sender: TObject );
    procedure SomeActionActionUpdate( Sender: TObject );
    procedure CloseActionExecute( Sender: TObject );
    procedure CloseActionUpdate( Sender: TObject );
    procedure SomeProgressActionActionExecute( Sender: TObject );
    procedure SomeProgressActionActionUpdate( Sender: TObject );
    procedure SomeRandomActionActionExecute( Sender: TObject );
    procedure SomeRandomActionActionUpdate( Sender: TObject );
  private
    FMain: WeakRef<TMainViewModel>;
  protected
    procedure AttachToViewModel( AViewModel: TViewModelBase ); override;
    procedure DetachFromViewModel( AViewModel: TViewModelBase ); override;
    procedure ViewModelPropertyChanged( Sender: TObject; const e: TPropertyChangedArgs ); override;
  public

  end;

var
  MainView: TMainView;

implementation

{$R *.fmx}

uses
  System.StrUtils,
  ViewModel.ActivityViewModel;

{ TMainView }

procedure TMainView.SomeProgressActionActionExecute( Sender: TObject );
begin
  inherited;
  FMain.Reference.SomeProgressActionCommand.Execute;
end;

procedure TMainView.SomeProgressActionActionUpdate( Sender: TObject );
begin
  inherited;
  TAction( Sender ).Enabled := FMain.IsAssigned and FMain.Reference.SomeProgressActionCommand.CanExecute;
end;

procedure TMainView.SomeRandomActionActionExecute( Sender: TObject );
begin
  inherited;
  FMain.Reference.SomeRandomActionCommand.Execute;
end;

procedure TMainView.SomeRandomActionActionUpdate( Sender: TObject );
begin
  inherited;
  TAction( Sender ).Enabled := FMain.IsAssigned and FMain.Reference.SomeRandomActionCommand.CanExecute;
end;

procedure TMainView.ViewModelPropertyChanged( Sender: TObject; const e: TPropertyChangedArgs );
begin
  inherited;

  if FMain.IsAssigned
  then
    begin

      if e.Matches( ['Active', 'DisplayName'] )
      then
        begin
          Caption := FMain.Reference.DisplayName + ' (' + IfThen( FMain.Reference.Active, 'Active', 'Inactive' ) + ')';
        end;

      if e.Match( 'Activity' )
      then
        begin
          ActivityCurtain.BringToFront;
          ActivityCurtain.Visible := Assigned( FMain.Reference.Activity );

          if FMain.Reference.Activity is TProgressActivityViewModel
          then
            begin
              ActivityView1.Visible := False;
              ActivityView1.SetViewModel( nil );
              ProgressActivityView1.Visible := True;
              ProgressActivityView1.BringToFront;
              ProgressActivityView1.SetViewModel( FMain.Reference.Activity );
            end
          else if FMain.Reference.Activity is TActivityViewModel
          then
            begin
              ProgressActivityView1.Visible := False;
              ProgressActivityView1.SetViewModel( nil );
              ActivityView1.Visible := Assigned( FMain.Reference.Activity );
              ActivityView1.BringToFront;
              ActivityView1.SetViewModel( FMain.Reference.Activity );
            end
          else
            begin
              ActivityView1.Visible := False;
              ActivityView1.SetViewModel( nil );
              ProgressActivityView1.Visible := False;
              ProgressActivityView1.SetViewModel( nil );
            end;
        end;

    end

end;

end.
Im Anhang nur die Exe
Angehängte Dateien
Dateityp: zip SimpleForm_exe.zip (2,04 MB, 16x 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