簡體   English   中英

當視圖為非模式視圖時,如何銷毀視圖之前的適配器?

[英]How do I destroy an adapter before a view when the view is non modal?

我為我的所有應用程序模塊都使用了某種(類似於MVA的)模式。

視圖是TForm后代:

TSomeView = class(TForm)
  ...
end;

數據在模型中管理:

TSomeModel = class
public
  property DataSet: TDataSet read ...;
end;

視圖和模型通過適配器粘合在一起。

uses
  Some.Model, Some.View;

type
TSomeAdapter = class
private
  FView  : TSomeView;
  FModel : TSomeModel;
  procedure ClickHandler(Sender: TObject);
public
  constructor Create(AOwner: TComponent);
  destructor Destroy; override;
  procedure Run;
end;

盡管這似乎有些乏味,但將各個部分分開工作非常有效。

到目前為止,我一直使用模態形式,因此適配器實現如下所示:

constructor TSomeAdapter.Create(AOwner: TComponent);
begin
  inherited Create;
  FModel := TSomeModel.Create,
  FView  := TSomeView.Create(AOwner);
  FView.DataSource.DataSet := FModel.DataSet;
  FView.SomeButton.OnClick := ClickHandler;
end;

procedure TSomeAdapter.Run;
begin
  FView.ShowModal;
end;

destructor TSomeAdapter.Destroy;
begin
  FView.Free;
  FModel.Free;
  inherited;
end;

這里重要的是,我不斷開析構函數中的事件處理程序和數據集,因為視圖始終首先被銷毀。

調用者使用以下模式創建應用程序模塊:

procedure CallSome;
var
  Adapter: TSomeAdapter;
begin
  Adapter := TSomeAdapter.Create(...);
  try
    Adapter.Run;
  finally
    Adapter.Free;
  end;
end;

我正在嘗試使它適應非模式形式。

調用者無法釋放適配器,因為它不知道何時。 因此,調用者代碼現在如下所示:

procedure CallSome;
var
  Adapter: TSomeAdapter;
begin
  Adapter := TSomeAdapter.Create(...);
  Adapter.Run;
  // Adapter is now a memory leak
end;

我不想更改銷毀順序,因為我依靠這樣的事實,即我不需要斷開處理程序和數據集的連接。

使用非模式形式時,如何保持銷毀順序(視圖<模型<適配器)?

您必須更改適配器,以便適​​配器是視圖的所有者,並在關閉時使視圖自毀。 當視圖將被銷毀時,適配器將收到通知。

type
  TSomeAdapter = class( TComponent )
  private
    FView : TSomeView;
    FModel : TSomeModel;
    FOnEndsRunning : TNotifyEvent;
    function GetModel : TSomeModel;
    function GetView : TSomeView;
    function GetIsRunning : Boolean;
  protected
    procedure Notification( AComponent : TComponent; Operation : TOperation ); override;
    property View : TSomeView read GetView;
    property Model : TSomeModel read GetModel;
    procedure ClickHandler( Sender : TObject );
  public
    destructor Destroy; override;
    procedure Run;
    property IsRunning : Boolean read GetIsRunning;
    property OnEndsRunning : TNotifyEvent read FOnEndsRunning write FOnEndsRunning;
  end;

{ TSomeAdapter }

destructor TSomeAdapter.Destroy;
begin
  FModel.Free;
  inherited;
end;

procedure TSomeAdapter.ClickHandler( Sender : TObject );
begin
  // DoSomething
end;

function TSomeAdapter.GetIsRunning : Boolean;
begin
  Result := Assigned( FView );
end;

function TSomeAdapter.GetModel : TSomeModel;
begin
  // lazy initialization of model
  if not Assigned( FModel ) then
    FModel := TSomeModel.Create;
  Result := FModel;
end;

function TSomeAdapter.GetView : TSomeView;
begin
  // lazy initialization of view
  if not Assigned( FView ) then
  begin
    FView := TSomeView.Create( Self ); // Owner is this Adapter
    FView.DataSource.DataSet := Model.DataSet;
    FView.SomeButton.OnClick := ClickHandler;
  end;
  Result := FView;
end;

procedure TSomeAdapter.Notification( AComponent : TComponent; Operation : TOperation );
begin
  inherited;
  case Operation of
    opInsert :
      ;
    opRemove :
      if AComponent = FView then
      begin
        // forget the view reference
        FView := nil;
        // destroy the model (optional)
        FreeAndNil( FModel );
        // notify
        if Assigned( OnEndsRunning ) then
          OnEndsRunning( Self );
      end;
  end;

end;

procedure TSomeAdapter.Run;
begin
  View.Show;
end;

視圖應該在關閉時釋放自己

TSomeView = class( TForm )
  procedure FormClose(Sender: TObject; var Action: TCloseAction);
end;

procedure TSomeView.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

現在,將通知您適配器是否結束運行

將主窗口句柄傳遞給所有適配器:

procedure CallSome;
var
  Adapter: TSomeAdapter;
begin
  Adapter := TSomeAdapter.Create(..., FMainView.Handle);
  FMainAdapter.Add(Adapter);
  Adapter.Run;
end;

附加OnClose並設置CloseActioncaNone

procedure TSomeAdapter.ViewClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  CloseAction := TCloseAction.caNone;
  PostMessage(FMainViewHandle, WM_FREE_ADATER, NativeUInt(@Self), 0);
end;

在主適配器中使用TObjectList並處理WM_FREE_ADAPTER:

constructor TMainAdapter.Create(...);
begin
  inherited;
  FAdapters := TObjectList.Create;
end;

procedure TMainAdapter.WMFreeAdapter(var Msg: TMessage);
begin
  FAdapters.Remove(PAdapter(Msg.WParam)^);
end;

destructor TMainAdapter.Destroy;
begin
  FAdapters.Free;
  inherited;
end;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM