Windows 中的一条经典规则是,您不能在焦点更改事件期间更改焦点。该OnDeactivate
事件发生在焦点更改事件期间。您的表单被告知它正在被停用——操作系统没有请求许可——同时,另一个表单被告知它正在被激活。两个窗口在这件事上都没有任何发言权,在这些事件发生时试图改变焦点只会让所有的窗口感到困惑。症状包括让两个窗口自己绘制,好像它们有焦点一样,并且尽管输入光标闪烁,但键盘消息却无处可去。MSDN更可怕,虽然我从未目睹过这么糟糕的事情:
在处理此消息 [ WM_KILLFOCUS ] 时,不要进行任何显示或激活窗口的函数调用。这会导致线程让出控制并可能导致应用程序停止响应消息。有关详细信息,请参阅消息死锁。
由于您无法在焦点更改已经开始后否认它,因此要做的是将事件处理延迟到事情稳定下来之后。当您的编辑表单被停用且其中的数据无效时,请在表单上发布一条消息。Posting 将消息放在消息队列的末尾,因此在所有先前的消息(尤其是焦点更改通知)都已处理之前,它不会被处理。当消息到达时,指示数据无效并将焦点重新设置回编辑表单:
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;
我应该指出,这个答案在技术上并不能回答您的问题,因为它不会阻止表单停用,但是您拒绝了我的另一个确实可以防止停用的答案。