Как я могу проверить, закрыта ли форма? - PullRequest
4 голосов
/ 27 января 2011

Единственный способ, которым я вижу, это добавить флаг для этого, но это лучший способ?

Когда форма уничтожена, и я проверяю, верно ли (Назначено (form2)) результат? Почему?

Как это сделать?

Ответы [ 5 ]

8 голосов
/ 27 января 2011

Вы можете использовать Form1.Show, чтобы увидеть, закрыта ли форма.

Простое закрытие формы не освобождает ее, если вы не установили Action := caFree в событии OnClose.По умолчанию caHide.

4 голосов
/ 27 января 2011

Если вы используете Form1.Free или Form1.Destroy, Delphi уничтожит объект, но не установит ссылку на объект равной nil.Поэтому вместо этого используйте FreeAndNil.

. Для получения дополнительной информации, проверьте ответ Андреаса Рейбранда и эту ссылку

4 голосов
/ 27 января 2011

Ух ты, взрыв из прошлого:)

Способ, которым Assigned () работает, заключается в том, что он в основном не проверяет указатель на ноль.Если вы уничтожите form2, все равно будет адрес памяти, на который указывает form2.

Я очень давно не делал Delphi, но по памяти вам нужно вручную установить var2 varдо нуля, когда он будет уничтожен.Если у вас есть центральное место (например, брокер форм?), Где вы создаете и уничтожаете формы, это должно быть довольно просто.

1 голос
/ 03 июля 2014

Столкнулся с той же проблемой при выполнении некоторых процедур при закрытии приложения. В этом случае все формы уничтожаются за сценой, но указатели не равны нулю. Этот код помогает мне:

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  MyForm:=nil;
end;

Таким образом, указатель становится нулевым, и я могу проверить его с помощью Assigned или сравнить с nil.

0 голосов
/ 24 марта 2017

Как совет, правильный способ сделать это в некоторых особых случаях - создать таймер, который присваивает переменной значение nil.

Я объясню (как-то это сложно), если вы создаете свою форму в своем собственном коде MyForm:=TMyForm.Create и у вас есть MyFrom.Close, это очень просто, просто добавьте MyForm:=nil или также лучше MyForm.FreeAndNil ... но иногда ссылка не какая-либо.

Пример: вы создаете внутри процедуры в цикле множество копий одной и той же формы (или только одной), позволяете форме открыться и завершите эту процедуру, теперь ссылка на открытую форму нигде не существует, так что вы можете не назначать ноль или делать freeandnil и т. д., как обычно.

В этом случае хитрость заключается в том, чтобы использовать таймер (всего одну миллисекунду), который делает это, этому таймеру нужна ссылка, поэтому вы должны хранить в глобальном, например, ссылку на себя, все, что можно сделать на при закрытии мероприятия.

Самый простой способ сделать бесплатное (когда нигде нет ссылок) - это создать TObjectList в главной форме, чтобы он содержал все ссылки на формы, которые должны быть свободными, и определял таймер (одну миллисекунду), который будет выполняться через этот список делает freeandnil; затем в onlcose добавьте Self в этот список и включите этот таймер.

Теперь другая часть, у вас есть нормальная форма, которая автоматически создается при запуске, но вам нужно установить ее на ноль и пересоздать ее по вашему собственному коду.

В этом случае есть глобальная точка, указывающая на эту форму, поэтому вам нужно только освободить и обнулить ее, но НЕ (я говорю это громко) в любой части внутри кода собственной формы, вы должны сделать это OUT (я говорю, если громко) формы кода.

Иногда вам нужно будет освободить форму, когда пользователь закроет ее, и она не отображается в модальном режиме, это сложный случай, но опять-таки тот же трюк действителен, при событии onclose вы включаете таймер (то есть из этой формы, обычно в основной форме) и этот таймер будет свободен и ноль. Этот интервал таймера может быть установлен как одна миллисекунда, он не будет работать до тех пор, пока форма полностью не закроется (пожалуйста, имейте в виду, что вы не используете Application.ProcessMessages, это обычно очень плохая идея).

Если вы установите для Self значение nil, free или что-либо другое в своей собственной форме, вы можете повредить память приложения (делая это совершенно небезопасно, не говоря уже о том, что это может съесть оперативную память).

Единственный способ освободить форму (и обнулить ее ссылку), форма, которая не отображается как модальная и является пользователем, закрывающим ее, - это запрограммировать триггер, который делает это после того, как форма полностью закрыта.

Я знаю о настройке действия, чтобы сделать бесплатное, но установить его на ноль другого безопасного пути нет.

Должен сказать: если вы используете таймеры в своей основной форме, запустите Enabled:=False для всех из них в событии Onclose ... в противном случае могут произойти странные вещи (не всегда, но иногда ... условия гонки по поводу уничтожения приложения) и запуск кода на этих таймерах) и, конечно, если кто-то был включен, действуйте правильно, чтобы правильно завершить его или прервать его и т. д.

Ваш вопрос - одна из сложных вещей, которую нужно сделать ... освободить и обнулить форму, закрытую не кодом, а действиями пользователя.

Для всего остального: Подумайте, если в приложении одновременно открыто много форм, и все могут одновременно взаимодействовать с пользователем (любой является модальным), и у вас есть код, который ссылается на некоторые из них из другие ... вы должны знать, что пользователь закрыл любую форму, чтобы избежать доступа к этой форме из кода. Это не тривиально, если вы не используете таймеры.

Если у вас есть «центральная» форма (например, приложение MDI), вы можете поместить этот таймер в основную форму MDI, так что любая закрытая дочерняя форма может быть освобождена и равна нулю, трюк снова является таймером на этой главной форма.

Только и только если вы уверены, что можете освободить и обнулить все невидимые формы, у вас может быть таймер на главной форме, который проходит через все формы, и если Visible имеет значение false, тогда вызовите FreeAndNil, я считаю, что этот способ склонен к ошибки, так как если вы добавите в будущем форму, которая не должна быть освобождена, но может оставаться скрытой ... этот код будет недействительным.

Всегда помните, что если пользователь является пользователем, который закрывает форму, которая должна быть освобождена, и ноль, то в коде нет способа обнаружить и действовать, событие не запускается (после того, как форма полностью закрыта) и перед формой полностью закрыт, вы даже не должны пытаться освободить его или обнулить его ссылку, могут произойти странные вещи (более склонные к этому, если материнская плата имеет более одного сокета, также больше, если ваше приложение использует потоки и т. д.).

Итак, для многопоточных приложений (а также не многопоточных) я использую другой метод, который отлично работает и не требует таймеров, но требует двойной проверки перед каждым ThatForm.*, трюк в том, чтобы определить булеву открытую переменную Form, например, как PleaseFreeAndNilMe в открытом разделе формы, затем в onclose (как в последней строке) установите значение True, а в OnCreate установите значение False.

Таким образом, вы узнаете, была ли эта форма закрыта или только скрыта (чтобы скрыть форму, никогда не вызывайте close, просто вызовите hide).

Таким образом, кодирование будет выглядеть так (вы можете использовать это как сглаживатель, вместо того, чтобы определять формы, так как TForm определяет их как TMyform, или, что еще лучше, использовать взлом как type TForm=class(Forms.TForm) вместо TMyForm=class(TForm), просто чтобы добавить эту переменную на все формы):

TMyForm=class(TForm)
...
public
      PleaseFreeAndNilMe:=Boolean;
...
procedure TMyForm.FormCreate(Sender: TObject);
begin
     PleaseFreeAndNilMe:=False;
     ...
end;
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
     ...
     PleaseFreeAndNilMe:=True;
end;

Если вы предпочитаете взломанную версию:

TForm=class(Froms.TForm)
public
      PleaseFreeAndNilMe:=Boolean;
end;
procedure TForm.FormCreate(Sender:TObject);
begin
     inherited Create(Sender);
     PleaseFreeAndNilMe:=False;
end;
procedure TForm.FormClose(Sender:TObject;var Action:TCloseAction);
begin
     PleaseFreeAndNilMe:=True;
     inherited FormClose(Sender,Action);
end;

Но, как я уже сказал, перед доступом к любому члену (или просто к тому, где вы выполняете сравнение с нулем) просто вызовите глобальную функцию, передающую ссылку (независимо от того, был ли он нулем или нет), закодированный как:

function IsNilTheForm(var TheForm: TMyForm);
begin
     if nil=TheForm
     then begin // The form was freed and nil
               IsNilTheForm:=True; // Return value
          end
     else begin // The form refence is not nil, but we do not know is it has been freed or not
               try
                  if TheForm.PleaseFreeAndNilMe
                  then begin // The form is not freed but wants to
                            try
                               TheForm.Free;
                            except
                            end;
                            try
                               TheForm:=Nil;
                            except
                            end;
                            IsNilTheForm:=True; // Return value
                       end
                  else begin // The form is not nil, not freed and do not want to be freed
                            IsNilTheForm:=False; // Return value
                       end; 
               except // The form was freed but not set to nil
                    TheForm:=Nil; // Set it to nil since it had beed freed
                    IsNilTheForm:=True; // Return value
               end;
          end;         
end;

Итак, где вы делаете if nil=MyForm then ..., теперь вы можете сделать if (IsNilTheForm(MyForm)) then ....

Вот и все.

Лучше решение по таймеру, поскольку форма освобождается как можно скорее (используется меньше оперативной памяти), с помощью трюка PleaseFreeAndNilMe форма не освобождается до вызова IsNilTheForm (если вы не освободите его где-либо еще).

То, что IsNilTheForm настолько сложно, потому что он учитывает все состояния (для материнской платы с несколькими сокетами и многопоточных приложений) и позволяет коду освободить / удалить его где-либо еще.

Конечно, эта функция должна вызываться в основном потоке и в атомарном исключении.

Освободить форму и обнулить ее указатель - нетривиальная вещь, в большинстве случаев, когда пользователь может закрыть ее в любое время (поскольку не запускается код вне формы).

Большая проблема заключается в следующем: когда пользователь закрывает форму, у него нет способа получить обработчик события, который запускается из этой формы, и после того, как форма завершает все свои действия.

Вообразите теперь, когда кодер поставил много Application.ProcessMessages; в любом месте приложения, а также в этой форме и т. д. ... и не позаботился об условиях гонки ... попробуйте освободить и обнулить такую ​​форму после того, как пользователь попросит ее закрыть ... это кошмар , но может быть решена с помощью взломанной версии TForm, в которой есть переменная, которая сообщает, что форма не была освобождена, но хочет ее.

Теперь представьте, что вы используете взломанную TForm и хотите нормальную TForm, просто определите ее как ...= class(Forms.TForm), так что теперь она будет иметь эту дополнительную переменную. Поэтому вызов IsNilTheForm будет действовать как сравнение с nil.

Надеюсь, что это помогает VCL-кодерам исправлять такие вещи, как, например, вызывать событие, когда объект уничтожается, освобождается, очищается, скрывается и т. Д., Из кода этого объекта, как в основной форме и т. Д. сделать жизнь проще ... или просто исправить это ... Close and Free подразумевает установку на Nil всех ссылок, которые указывают на это.

Есть еще одна вещь, которую можно сделать (но я всегда стараюсь ее избегать): иметь несколько переменных, которые указывают на одну и ту же форму (не на копии), которая подвержена множеству ошибок, вы свободны один и нужно обнулить их все и т. д. Код, который я показываю, также совместим с этим.

Я знаю, что код закончен ... но Free и Nil форма сложнее, чем мой код.

...