Предотвращение деактивации формы в Delphi 6 - PullRequest
2 голосов
/ 05 октября 2009

У нас есть приложение Delphi 6, которое использует немодальную форму с редактированием в сетке. В рамках события FormClose мы проверяем, что записи являются квадратными, и предотвращаем закрытие, если это не так.

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

Я пробовал событие FormDeactivate, которое запускается, но, похоже, не имеет никакого механизма предотвращения деактивации (в отличие от параметра Action событий FormClose). Я пробовал OnExit из сетки, но он не срабатывает при деактивации. Я пытался перехватить сообщение WM_ACTIVATE и установить Msg.Result = 1, но это не имеет никакого эффекта (возможно, потому что другое сообщение WM_ACTIVATE отправляется в основную форму?).

Итак, я ищу идеи о том, как (условно) предотвратить деактивацию формы, когда пользователь нажимает на другую форму. (PS Я не хочу менять стиль формы на fsStayOnTop)

Спасибо

Ответы [ 6 ]

1 голос
/ 05 октября 2009

Классическим правилом в Windows является то, что вы не можете изменить фокус во время события с изменением фокуса. Событие OnDeactivate происходит во время изменения фокуса. Вашей форме сообщают, что она деактивирована - операционная система не запрашивает разрешения - и в то же время другой форме сообщается, что она активируется. Ни одно окно не имеет права голоса в этом вопросе, и попытка изменить фокус во время этих событий только запутает все окна. Симптомы включают в себя то, что два окна закрашивают себя так, как если бы они были в фокусе, и сообщения клавиатуры не идут никуда, несмотря на мигание курсора ввода. MSDN еще страшнее, хотя я никогда не был свидетелем ничего плохого:

При обработке этого сообщения [ WM_KILLFOCUS ] не выполняйте никаких вызовов функций, которые отображают или активируют окно. Это приводит к тому, что поток возвращает управление, и приложение может перестать отвечать на сообщения. Для получения дополнительной информации см. Блокировка сообщений .

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

const
  efm_InvalidData = wm_User + 1;

type
  TEditForm = class(TForm)
  ...
  private
    procedure EFMInvalidData(var Msg: TMessage); message efm_InvalidData;
  end;

procedure TEditForm.FormDeactivate(Sender: TObject);
begin
  if DataNotValid then
    PostMessage(Handle, efm_InvalidData, 0, 0);
end;

procedure TEditForm.EFMInvalidData(var Msg: TMessage);
begin
  Self.SetFocus;
  ShowMessage('Invalid data');
end;

Я должен отметить, что этот ответ технически не отвечает на ваш вопрос, поскольку он ничего не делает для предотвращения деактивации формы, но вы отклонили мой другой ответ , который действительно предотвращает деактивацию.

1 голос
/ 05 октября 2009

Когда вы звоните ShowModal, все формы, кроме показанной, отключаются. Они включаются как раз перед возвращением ShowModal.

Показать вашу форму редактирования немодально , и когда данные начинают редактироваться, сделайте форму модальной, отключив другую форму. Включите другую форму после завершения редактирования. Очевидно, что отключение окон не всегда так просто, как установка свойства Enabled. Я бы предложил использовать DisableTaskWindows, но это отключило бы все окна, включая вашу форму редактирования. Тем не менее, посмотрите, как это реализовано в Forms.pas . Он хранит список всех окон, которые он отключает, так что только после этого они снова включаются.

0 голосов
/ 06 октября 2009

Спасибо всем за помощь и предложения.

Вот решение, с которым я столкнулся: в «сетке» (например, Form2) ...

public  
    PricesNotSquare: boolean;  

в FormDeactivateсобытие, установите PriceNotSquare в true, если они не совпадают.

В событии OnActivate главной формы ...

  if Assigned(Form2) and (Form2.PricesNotSquare) then  
  begin  
      ShowMessage( 'Please ensure the total Prices match before leaving the form' );  
      Form2.Show;  
      exit;  
  end;  
  // other form activate stuff here  

Оказалось простым решением, просто потребовалоськакое-то время, чтобы получить его.

Кажется, что он работает нормально, но если у него есть проблемы, я включу идею отправки сообщения.

0 голосов
/ 05 октября 2009

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

Даже если вам удастся найти событие / метод / сообщение, которое делает то, что вы хотите, вам все равно придется иметь дело со сценарием, в котором отключается электричество.

На чуть более полезной заметке вы пытались отключить вашу mainform до готовности? Вы можете поместить все элементы управления на панель, а затем просто сделать

panel1.enabled := false;
0 голосов
/ 05 октября 2009

Delphi 2006 представил события OnMouseActivate. OnMouseActivate основной формы позволит вам предотвратить активацию основной формы, если другая форма видима.

Конечно, это не работает с D6.

0 голосов
/ 05 октября 2009

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

[Редактировать] Копия моего комментария:

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

Для уточнения регистрации события:

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

Псевдокод для программного добавления события!

myform.onShow=myGrid.formOnShowHandler;

formOnShowHandler имеет ту же сигнатуру, что и функции, сгенерированные IDE для событий onShow. У него есть параметр, который вы можете использовать, чтобы выяснить, какая форма вызвала обработчик, так что вы можете повторно использовать функцию и просто поместить форму в фоновый режим и снова показать свою форму сетки (которая будет, например, родителем сетки).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...