Dane zwracane a ShowModal

0

Mam pytanie. Czy sposób w który otrzymuje dane z okna modalnego jest poprawny. Dodam tylko że działa to i nie mam błędów ale czy czasami mogą być jakieś wycieki?

Procedure Button1
Var
  Result : String;
  Form : TForm;
Begin
  Form := TForm.Create(Application);
  Form.ShowModal;
  Result := Form.Return;
  FreeAndNil(Form);
End;

A tutaj kod formularza:

unit UForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    Return : String;
  published
    { Public declarations }
  end;

implementation

{$R *.dfm}

procedure TForm.Button1Click(Sender: TObject);
begin
   Return := Edit1.Text;
   Close;
end;

procedure TForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := CaFree;
end;

end.
0

Zwykle przyjęło się wpisywanie bezpośrednio do Result wartości zwracanej modalnie, np.:

Result := Form.ShowModal();

pod warunkiem, że Result jest co najmniej typu TModalResult;</del>

Źle zrozumiałem to, o co pytasz...


Twój sposób jest raczej dobry, choć ja zawsze wolę zrobić sobie dodatkową klasę w module z danym formularzem, która tworzy i wywołuje okno modalne, oraz przechwytuje przed jego zamknięciem wartość zwracaną (oprócz tej typu TModalResult, żebym mógł rozróznić czy użytkownik zamknął okno krzyżykiem, czy faktycznie wybrał jakieś tam dane); Taka klasa jest dość skromna, ale daje możliwość operowania na obiekcie wykorzystującym formularz, bez bawienia się w widocznośc;

Jeśli byłbyś zainteresowany stworzeniem własnego prostego okna modalnego, które dawałoby możliwość zwrócenia danych dowolnego typu - zapoznaj się z moim artykułem omawiającym podstawy tworzenia własnych okien dialogowych.

0

TForm = class(TForm) ? zmieniałeś nazwy zanim wkleiłeś?

0

Tak pisałem to z głowy stąd ta "literówka" -> TForm

0

Jest poprawny pod względem że działa i wycieku pamięci nie będzie ale wg. mnie bardziej elegancko jest to zrobić w ten sposób:
W module w którym jest przycisk tworzący i pokazujący modalny formularz:

  private
    { Private declarations }
    fFormModal: TfrmModal;
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}


procedure TfrmMain.btnShowModalFormClick(Sender: TObject);
var
  Result: string;

begin
  if not Assigned(fFormModal) then
    fFormModal:= TfrmModal.Create(Self);
  if fFormModal.ShowModal(Result) = mrOK then
    ShowMessage(Result); //tu mozna uzyc zmiennej
  fFormModal:= nil; //tu wystarczy zwykle przypisanie nil formularz i tak bedzie zwolniony
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  fFormModal:= nil;
end;

W module modalnego formularza:

  private
    { Private declarations }
    fReturn: string;
  public
    { Public declarations }

    function ShowModal(var AReturn: string): Integer; overload;
  end;

var
  frmModal: TfrmModal;

implementation

{$R *.dfm}

function TfrmModal.ShowModal(var AReturn: string): Integer;
begin
  result:= ShowModal;
  AReturn:= fReturn;
end;

procedure TfrmModal.btnCancelClick(Sender: TObject);
begin
  ModalResult:= mrCancel;
end;

procedure TfrmModal.btnOkClick(Sender: TObject);
begin
  fReturn:= Edit1.Text;
  ModalResult:= mrOK;
end;

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

procedure TfrmModal.FormCreate(Sender: TObject);
begin
  fReturn:= '';
end;

Dodam jeszcze że nawet gdyby w OnClose modalnego formularza nie było przypisania Action:= caFree; to wycieku pamięci i tak nie będzie, bo za jego zwolnienie będzie odpowiadał obiekt (Form lub Application) który podasz jako parametr przy jego tworzeniu (no chyba że podasz nil to oczywiście obowiązkowo musisz gdzieś zwolnić). Tyle że pamięć zostanie zwolniona dopiero gdy tamten obiekt zostanie zwolniony. Natomiast jeżeli chcesz zwalniać formularz zaraz po jego zamknięciu musi być Action:= caFree lub ewentualnie zwplnienie musi być zrobione w innym miejscu np. przez wywołanie FreeAndNil(fFormModal);

0

Można jeszcze tak:

type
  TModalEdit=class(TForm)
    Dane:TEdit;
    BtnOk:TButton;
    BtnCancel:TButton;
  private
  public
    class function Execute(var Value:String):Boolean;
  end;

implementation

class function TModalEdit.Execute(var Value:String):Boolean;
begin
  with Create(Application) do
  begin
    try
      Result:=(ShowModal=mrOk);
      if Result then Value:=Dane.Text;
    finally
      Free;
    end;
  end;
end;

odpalenie:

var Dane:String;
begin
  if TModalEdit.Execute(Dane) then
  begin
    // Wpisano Dane i naciśnięto przycisk <Ok>
  end;
end;
0

Dziękuję Wam wszystkim, podsunęliście ciekawe pomysły, łatwiejsze i praktyczniejsze w użyciu.

0

Jedna z najbardziej słusznych metod:

function ShowMyModalForm(var data: string): boolean;
var
  f: TMyModalForm;
begin
  f:=TMyModalForm.Create(Application);
  result:=(f.showModal = mrOK);

  if result then
  begin
    data:=f.FData;
  end;

  FreeAndNil(f);
end;

Do tego tworzymy conajmniej jeden przycisk, który ma ModalResult ustawione na mrOK (ewentualnie ustawiamy w kodzie: ModalResult:=mrOK);
Żadnego caFree - to forma modalna, jest zwalniana podczas FreeAndNil. Myślę, że kod podany przeze mnie jest dość jasny.

0

To ja się czepię artykułu Furious(pierwszy przykład). Masz tam w onClose:

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

Wiesz, czemu to działa? Tylko dlatego, że InfoBoxForm jest zmienną globalną tworzoną automatem przez środowisko. Twój obiekt znajduje się w InfoBoxForm, ale zadeklarowanej LOKALNIE w funkcji InfoBox. Poza tym masz w bloku finally: InfoBoxForm.Free, co jest już zbędne(bo masz caFree). O co chodzi. Gdybyś tworzył obiekt w globalnej zmiennej InfoBoxForm, to prowadzi do jednej drogi. AccessViolation. Dlaczego? Bo najpierw nilujesz obiekt(onClose), a potem... no właśnie, co się dzieje potem? On chce się zwolnić, bo ma caFree, ale nie może, bo nie istnieje. Więc pojawia się AccessViolation. Drugą możliwością jest po prostu wystąpienie memory leaka, bo ten obiek się nigdy nie zwolni.
Przypisanie: Action:=caFree NIE JEST tożsame z wywołaniem obiekt.Free. To jest tylko taka wskazówka dla obiektu: "Słuchaj, jak się już zamkniesz, to weź się zwolnij". Coś jak dla wątku przypisanie: FreeOnTerminate:=true.

I jeszcze raz - jeśli pobieracie zmienne z obiektu formularza po ShowModal, to nie używajcie caFree, tylko tak, jak napisałem wyżej.

1

Poniżej przedstawiam mój sposób wykorzystania okien dialogowych; Mam nadzieję, że nic nie pomyliłem (pisałem z pamięci) - w razie nieprawidłowości poprawię; To okno dialogowe służy przykładowo do pobrania listy plików - pozycji z kontrolki klasy TListBox; Po wybraniu odpowiednich pozycji i wciśnięciu przycisku btnAdd są kopiowane do pola FFormFilesList w klasie formularza, następnie przed zwolnieniem formularza z pamięci lista plików zawarta w klasie formularza jest kopiowana do listy zawartej w klasie dialogu;

Nie trzeba tworzyć dodatkowej listy w klasie formularza, bo w zdarzeniu OnClose można kopiować bezpośrednio z kontrolki - jak kto woli; Chciałem tym jedynie pokazać w jaki sposób lista tych plików wędruje z formularza (TFilesForm.FFormFilesList) do klasy dialogu (TFilesDialog.FDialogFilesList), a z kolei z niej do funkcji wykorzystującej dialog (funkcja UseFilesDialog, argument FilesList); Obie klasy muszą być w jednym module;

Deklaracja klasy formularza

type
  TFilesForm = class(TForm)
    lbFiles: TListBox;
    btnAdd: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnAddClick(Sender: TObject);
  private
    FFormFilesList: TStrings;
  end;

Definicje zdarzeń

procedure TFilesForm.FormCreate(Sender: TObject);
begin
  FFormFilesList := TStringList.Create();
end;

procedure TFilesForm.FormDestroy(Sender: TObject);
begin
  FFormFilesList.Free();
end;

procedure TFilesForm.btnAddClick(Sender: TObject);
begin
  for I := 0 to Pred(lbFiles.Items.Count) do
    if lbFiles.Selected[I] then
      FFormFilesList.Add(lbFiles.Items[I]);

  if FFormFilesList.Items.Count = 0 then
    ShowMessage('Nie wybrano plików!')
  else
    ModalResult := mrOk;
end;

Deklaracja klasy dialogu zarządzającego formularzem

type
  TFilesDialog = class(TObject)
  private
    FFilesForm: TFilesForm;
    FDialogFilesList: TStrings;
    procedure DialogClose(Sender: TObject; var Action: TCloseAction);
  public
    constructor Create();
    destructor Destroy(); override;
    function Execute(Owner: TComponent): Boolean;
  public
    property FilesList: TStrings read FDialogFilesList;
  end;

Definicje metod

constructor TFilesDialog.Create();
begin
  inherited Create();

  FFilesForm := nil;
  FDialogFilesList := TStringList.Create();
end;

destructor TFilesDialog.Destroy();
begin
  FDialogFilesList.Free();
  inherited Destroy();
end;

procedure TFilesDialog.DialogClose(Sender: TObject; var Action: TCloseAction);
var
  I: Integer;
begin
  if FFilesForm.FFormFilesList.Count > 0 then
    FDialogFilesList.Assign(FFilesForm.FFormFilesList);

  //Action := caFree;
end;

function TFilesDialog.Execute(Owner: TComponent): Boolean;
begin
  if not Assigned(FFilesForm) then
    FFilesForm := TFilesForm.Create(Owner);

  FFilesForm.OnClose := DialogClose;
  Result := FFilesForm.ShowModal = mrOk;
end;

Przykładowa funkcja wykorzystująca klasę dialogu

function UseFilesDialog(Owner: TComponent; var{out} FilesList: TStrings): Boolean;
var
  FilesDlg: TFilesDialog;
begin
  FilesDlg := TFilesDialog.Create();

  try
    Result := FilesDlg.Execute(Owner);

    if FilesDlg.FilesList.Count > 0 then
      FilesList.Assign(FilesDlg.FilesList);
  finally
    FilesDlg.Free();
  end;
end;

Przykład użycia powyższej funkcji

procedure TForm1.Button1Click(Sender: TObject);
var
  slFiles: TStrings;
begin
  slFiles := TStringList.Create();

  try
    if UseFilesDialog(Self, slFiles) then
      { wykorzystanie listy plików z slFiles }
  finally
    slFiles.Free();
  end;
end;

Metoda według mnie bezpieczna, daje możliwość ręcznego zarządzania tworzonym i zwalnianym dynamicznie formularzem, a także zwracania dowolnych danych, nie wliczając w to ModalResult; Mnie ta metoda nigdy nie zawiodła - nie miałem problemów z wyciekami pamięci czy niewiadomego pochodzenia AV; Jednak w/w kodu nie kompilowałem i nie testowałem, więc mogłem coś pomylić - jakby coś było nie tak - poprawię.

1 użytkowników online, w tym zalogowanych: 0, gości: 1