Frage

Ich habe eine MDI-Anwendung geschrieben in Delphi 2007.

Wenn der Benutzer verlässt ein Formular in ihr während Code ausgeführt wird es eine Ausnahme verursacht, weil der Code versucht, eine Komponente zu aktualisieren oder ein Objekt verwenden, die mit der Form befreit worden ist.

Gibt es trotzdem kann ich feststellen, ob Code in dem Exit-Ereignisse ausgeführt wird oder ist es eine Standardmethode mit dieser Situation umgehen?

Update mit mehr Information

Die Ausnahme in der Regel im folgenden Umständen etwas passieren.

Eine Taste auf der Kind mdi Form gedrückt wird, diese Funktion in Form aktiviert, wird die Funktion in die Datenbank gehen und Abrufen von Daten, es wird dann neu formatieren und sie in einer visuellen Komponente auf dem Formular anzuzeigen ( verwendbar ein TListView).

Wenn der Code eine lange Zeit nimmt auszuführen (sagen wir, wenn es eine Menge von Daten zu verarbeiten) wird der Benutzer das Interesse verlieren und klicken Sie auf die Schaltfläche zum Schließen (die Geschwindigkeit des Codes wurde gearbeitet, um zu versuchen, dies zu vermeiden ).

Der Code innerhalb der Funktion noch ausgeführt wird, auch wenn die Form gehört es sich befreit hat (Der Code ist im privaten Abschnitt des Formulars), jetzt, wenn es trys die visuellen Komponenten aktualisieren sie nicht mehr (wie sie existieren wurden mit der Form befreit), und es wirft eine Ausnahme.

Der Code in dem Kind Form ist benutzbar in einer Schleife, wenn dieses Radfahren Aufzeichnungen passieren, und aktualisieren Sie die Listenansicht entsprechend, die Schleifen-Code enthält, der wie so aussieht

inc(i);
if (i mod 25) = 0 then
begin
    StatusPnl.Caption := 'Loading ' + intToStr(i) + ', Please wait';
    application.ProcessMessages;
end;

Weitere Codebeispiele

das fromClose Ereignis sieht so aus

//Snip
if (Not (Owner = nil)) then
with (Owner as  IMainForm)do
begin
    //Snip
    DoFormFree(Self,Self.Name);
end
else
//Snip

DoFormFree ist eine Funktion im Haupt mdi Mutterform und sieht aus wie so

//Snip
(G_FormList.Objects[x] as TBaseForm).Release;
G_FormList.Objects[i] := nil;
G_FormList.Delete(i);
//Snip

sind alle Formen in einer Liste gespeichert, wie aus verschiedenen Gründen, und alle untergeordneten Formulare erweitern die TBaseForm Klasse.

Im Idealfall möchte ich einen Weg sagen, ob Code in einer Form ausgeführt wird, und verhindert, dass der Benutzer das Formular zu schließen, oder ausblenden, bis der Code abgeschlossen ist, wie in einigen Fällen kann es einen Bericht und Aktualisierung zu erzeugen, wie Statusanzeige, wenn die Ausnahme geschehen, in diesem Fall wird der Bericht unvollständig sein.

als alle Formen sind Unterklassen von TbaseFrom einige globale Art und Weise, dies zu tun wäre ideal, so kann ich den Code an die Grundform hinzufügen und haben es auf alle abstammen Formen arbeiten.

War es hilfreich?

Lösung

Sie bieten nicht genügend Informationen, aber die einfachste Lösung, die den Sinn kommt, ist in der OnCloseQuery Handler zu testen, ob Code ausgeführt wird, und wenn ja CanClose auf False festgelegt.

Alternativ können Sie den Code aus dem MDI-Formular entkoppeln, indem ein Zwischenobjekt zu schaffen, dass sowohl die Form und der Hintergrund-Code kennen. Sie lassen dies einen Verweis auf das Formular, das zurückgesetzt wird, wenn die Form geschlossen ist. Durch Routing alle Zugriff auf das Formular durch dieses Zwischen Objekt Sie die Ausnahmen verhindern.

Edit: Sie benötigen Informationen geben, wie Sie den Code ausführen, der die MDI-Formular zuzugreifen versucht, nachdem er befreit wurde. Es gibt einige Möglichkeiten Arbeiter Code auszuführen, wie:

  • in einem Verfahren der Form oder eines anderen Objekts
  • in einem OnTimer Event-Handler
  • in dem OnIdle-Handler des Application-Objekts
  • in einem Hintergrund-Thread

Beachten Sie im ersten Fall, dass das Formular nur befreit werden, wenn Sie es entweder im Code selbst tun, oder wenn Sie anrufen Application.ProcessMessages. Ohne weitere Informationen über das, was wie Ihr Code sieht, kann niemand geben Sie eine bestimmte Antwort auf Ihre Frage.

Edit 2: Mit Ihren zusätzlichen Informationen scheint es, dass der Code in Frage immer in den Verfahren der Form ausgeführt wird. Dies ist einfach durch die Schaffung eines boolean Mitglied zu fangen, die auf True gesetzt, wenn die Ausführung beginnt, und dass auf False gesetzt, wenn die Ausführung beendet hat. Nun müssen Sie nur einen Handler für OnCloseQuery in Ihrer Basisklasse hinzuzufügen, und legen Sie CanClose auf False, wenn das Element (fExecuting zum Beispiel) Wahr ist. Sie können Schließen leise verbieten, oder ein Informationsfeld angezeigt. Ich würde einfach einen Fortschritt Form zeigen oder etwas in der Statusleiste angezeigt, um den Benutzer nicht zu viel mit modalen Infokästen zu unterbrechen.

Was würde ich auf jeden Fall tun wird der Benutzer so dass der lange laufenden Prozess abzubrechen. So könnte man auch ein Meldungsfeld zeigen, fragt die Benutzer, ob sie den Betrieb und in der Nähe abbrechen möchte. Sie müssen noch die Schließung der Form dann überspringen, aber die Anfrage speichern zu schließen, und es verarbeiten, wenn die Ausführung beendet ist.

Andere Tipps

Jede Form hat ein OnCloseQuery Ereignis.

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

Sie können diese verwenden Nähe verschieben von CanClose auf False festlegen.

Sie müssen entscheiden, ob Sie die Nähe zu handhaben wollen, bis die Verarbeitung beendet ist. Oder Sie könnten den Benutzer auffordern wieder zu schließen.

Führen privaten Bereich in MDI-Formular zB. FProcessing

in db Anruf Code tun:

FProcessing := true;
try
  i := 0;  
  if (i mod 25) = 0 then
  begin
    // do your code 
    Application.ProcessMessages; 
  end;
finally
  FProcessing := false; 
end;

in MDIForm.FormCloseQuery ()

procedure TMDIForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if FProcesing then 
    CanClose := False;  
   // or you can ask user to stop fetching db data
end;

Sie sollten ganze app terminaation Aslo überprüfen.

Ich habe ein Objekt, das ein Verfahren oder eine Methode für Sie ohne Verwendung eines Thread ausführen kann. Es verwendet einen Timer, sondern setzt nur einen einfachen Zeilenaufruf. Es unterstützt auch RTTI, so dass Sie einfach in eine Schaltfläche klicken Sie entweder setzen können:

ExecuteMethodProc (MyCode) oder ExecuteMethodName ( 'MyCode');

Viele Grüße, Brian

// Method execution
//-----------------------------------------------------------------------------

type
  TArtMethodExecuter = class( TObject )
    constructor Create;
    destructor  Destroy; override;
  PRIVATE

    FMethod           : TProcedureOfObject;
    FTimer            : TTimer;
    FBusy             : boolean;
    FFreeAfterExecute : boolean;
    FHandleExceptions : boolean;

    procedure DoOnTimer( Sender : TObject );
    procedure SetBusy( AState : boolean );

  PUBLIC
    procedure ExecuteMethodProc(
                AMethod       : TProcedureOfObject;
                AWait         : boolean = False );

    procedure ExecuteMethodName(
                AMethodObject : TObject;
          const AMethodName   : string;
                AWait         : boolean = False );

    property  FreeAfterExecute : boolean
                read FFreeAFterExecute
                write FFreeAfterExecute;

    property  HandleExceptions : boolean
                read FHandleExceptions
                write FHandleExceptions;

    property  Busy : boolean
                read FBusy;

  end;





procedure ExecuteMethodName(
            AMethodObject : TObject;
     const  AMethodName    : string;
            AHandleExceptions : boolean = True );
// Executes this method of this object in the context of the application.
// Returns immediately, with the method executing shortly.

procedure ExecuteMethodProc(
            AMethodProc : TProcedureOfObject;
            AHandleExceptions : boolean = True );
// Executes this method of this object in the context of the application.
// Returns immediately, with the method executing shortly.

function  IsExecutingMethod : boolean;
// Returns TRUE if we are already executing a method.


// End method execution
//-----------------------------------------------------------------------------




// Method execution
//-----------------------------------------------------------------------------


{ TArtMethodExecuter }

var
  iMethodsExecutingCount : integer = 0;

const
  wm_ExecuteMethod = wm_User;

constructor TArtMethodExecuter.Create;
begin
  Inherited;
end;

destructor TArtMethodExecuter.Destroy;
begin
  FreeAndNil( FTimer );
  Inherited;
end;

procedure TArtMethodExecuter.DoOnTimer( Sender : TObject );

  procedure RunMethod;
  begin
    try
      FMethod
    except
      on E:Exception do
        ArtShowMessage( E.Message );
    end
  end;

begin
  FreeAndNil(FTimer);
  try
    If Assigned( FMethod ) then
      RunMethod
     else
      Raise EArtLibrary.Create(
        'Cannot execute method - no method defined.' );
  finally
    SetBusy( False );
    If FFreeAfterExecute then
      Free;
  end;
end;



procedure TArtMethodExecuter.SetBusy(AState: boolean);
begin
  FBusy := AState;

  If AState then
    Inc( iMethodsExecutingCount )
   else
    If iMethodsExecutingCount > 0 then
      Dec( iMethodsExecutingCount )
end;



procedure TArtMethodExecuter.ExecuteMethodProc(
          AMethod       : TProcedureOfObject;
          AWait         : boolean = False );
begin
  SetBusy( True );
  FMethod         := AMethod;
  FTimer          := TTimer.Create( nil );
  FTimer.OnTimer  := DoOnTimer;
  FTimer.Interval := 1;
  If AWait then
    While FBusy do
      begin
      Sleep( 100 );
      Application.ProcessMessages;
      end;
end;



procedure TArtMethodExecuter.ExecuteMethodName(AMethodObject: TObject;
  const AMethodName: string; AWait: boolean);
var
  RunMethod : TMethod;
begin
  RunMethod.code := AMethodObject.MethodAddress( AMethodName );
  If not Assigned( RunMethod.Code ) then
    Raise EArtLibrary.CreateFmt(
      'Cannot find method name "%s". Check that it is defined and published.', [AMethodName] );

  RunMethod.Data := AMethodObject;
  If not Assigned( RunMethod.Data ) then
    Raise EArtLibrary.CreateFmt(
      'Method object associated with method name "%s" is not defined.', [AMethodName] );

  ExecuteMethodProc(
    TProcedureOfObject( RunMethod ),
    AWait );
end;


procedure ExecuteMethodName(
            AMethodObject : TObject;
      const AMethodName   : string;
            AHandleExceptions : boolean = True );
// Executes this method of this object in the context of the application.
// Returns immediately, with the method executing shortly.
var
  ME : TArtMethodExecuter;
begin
  If IsExecutingMethod then
    If AHandleExceptions then
      begin
      ArtShowMessage( 'A method is already executing.' );
      Exit;
      end
     else
      Raise EArtLibrary.Create( 'A method is already executing.' );

  ME := TArtMethodExecuter.Create;
  ME.FreeAfterExecute := True;
  ME.HandleExceptions := AHandleExceptions;
  ME.ExecuteMethodName( AMethodObject, AMethodName );
end;


procedure ExecuteMethodProc(
            AMethodProc : TProcedureOfObject;
            AHandleExceptions : boolean = True );
// Executes this method of this object in the context of the application.
// Returns immediately, with the method executing shortly.
var
  ME : TArtMethodExecuter;
begin
  If IsExecutingMethod then
    If AHandleExceptions then
      begin
      ArtShowMessage( 'A method is already executing.' );
      Exit;
      end
     else
      Raise EArtLibrary.Create( 'A method is already executing.' );

  ME := TArtMethodExecuter.Create;
  ME.FreeAfterExecute := True;
  ME.HandleExceptions := AHandleExceptions;
  ME.ExecuteMethodProc( AMethodProc );
end;

function  IsExecutingMethod : boolean;
// Returns TRUE if we are already executing a method.
begin
  Result := iMethodsExecutingCount > 0;
end;

// End Method execution
//-----------------------------------------------------------------------------

Wenn der Benutzer bis geben will, weil der Vorgang so lange dauert, sie, warum sie nicht erlauben? Ändern Sie den Code leicht zu überprüfen (direkt vor den application.process Nachrichten ist ein guter Ort) ein „will aufhören“ Variable, und wenn es wahr ist, dann von der Schleife zu retten, befreien Sie Ihre Objekte und stornieren. Dann wickeln Sie diese in dem, was dmajkic früher vorgeschlagen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top