我们有一个Delphi 6应用程序,它使用非模态形式和网格编辑。在FormClose事件中,我们检查条目是否为正方形,如果不是则阻止关闭。
然而,如果用户点击后面的主表单,那么原始表单就会消失(正如您所期望的那样),但这允许用户移动到主屏幕上的新记录,而不会在网格中进行更改已经过验证。
我已经尝试过FormDeactivate事件,它会触发但似乎没有任何机制来阻止停用(与FormClose事件Action参数不同)。 我从网格中尝试了OnExit,但是在停用时它不会触发。 我试图捕获WM_ACTIVATE消息并设置Msg.Result = 1但这没有效果(可能是因为另一个WM_ACTIVATE消息正在发送到主窗体?)。
所以,我正在寻找关于如何(有条件地)阻止用户点击另一个表单时停用表单的想法。 (PS我不想将表单样式更改为fsStayOnTop)
由于
答案 0 :(得分:1)
当您致电ShowModal
时,除了正在显示的表单之外的所有表单都将被禁用。它们会在ShowModal
返回之前重新启用。
显示您的编辑表单非模式,当数据开始编辑时,通过禁用其他表单使表单自己成为模态。编辑完成后启用其他表单。显然,禁用窗口并不总是像设置Enabled
属性那么简单。我建议使用DisableTaskWindows
,但它会禁用所有窗口,包括您的编辑表单。尽管如此,请看一下它在 Forms.pas 中的实现方式。它保留了它禁用的所有窗口的列表,以便之后只能重新启用它们。
答案 1 :(得分:1)
Windows中的经典规则是在焦点更改事件期间无法更改焦点。在焦点更改事件期间发生OnDeactivate
事件。您的表单被告知它正在被停用 - 操作系统不会要求许可 - 同时,另一个表单被告知它正在被激活。这两个窗口都没有任何发言权,并且在这些事件发生时尝试改变焦点只会让所有窗口混乱。症状包括让两个窗口自己绘制,就好像它们有焦点一样,并且尽管输入光标闪烁,但键盘消息仍无处可寻。 MSDN更加可怕,尽管我从未目睹过任何不好的事情:
处理此消息[WM_KILLFOCUS]时,请勿进行任何显示或激活窗口的函数调用。这会导致线程产生控制并导致应用程序停止响应消息。有关详细信息,请参阅Message Deadlocks。
由于你不能在它已经开始之后否认焦点变化,所以要做的是延迟事件的处理,直到事情稳定下来。当您的编辑表单停用并且其中的数据无效时,发布表单消息。发布将消息放在消息队列的末尾,因此在所有先前的消息(特别是焦点更改通知)已经处理之前,它将不会被处理。消息到达时,指示数据无效并将焦点设置回编辑表单:
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;
我应该指出,这个答案在技术上并没有回答你的问题,因为它没有阻止表单停用,但你拒绝my other answer确实阻止了停用。
答案 2 :(得分:0)
您还可以在模型中引入一个状态,用于跟踪窗口是否需要您在此处描述的焦点,并在其他表单上使用onFocus处理程序,以编程方式将焦点设置回网格窗口。
[编辑]我的评论副本:
您可以使用网格注册表单的onShow事件。 (如果你实现它,一定要使它以某种方式配置,以最小化网格对应用程序的当前布局的依赖。可能通过提供一个由窗体调用的方法,这反过来触发调用窗体的网格事件注册对于onShow事件)
详细说明事件登记:
您可以以编程方式附加事件处理程序。网上有很多关于这个的消息。我这里没有Delphi,所以我现在无法复制工作代码。
以编程方式附加事件的伪代码!
myform.onShow=myGrid.formOnShowHandler;
formOnShowHandler与IDE为onShow事件生成的函数具有相同的签名。它有一个参数,您可以使用该参数来确定哪个表单调用了处理程序,以便您可以重用该函数并简单地将表单放在后台并再次显示您的gridform(例如,它将是网格的父级)。 / p>
答案 3 :(得分:0)
Delphi 2006引入了OnMouseActivate事件。如果另一个表单可见,主窗体的OnMouseActivate将阻止主窗体被激活。
当然,这不适用于D6。
答案 4 :(得分:0)
这不是一个有用的答案大卫,但我想我必须同意其他受访者认为这不是正确的方法。有很多方法可以让所有人都出错,停下来并以不同的方式来解决你的问题可能会更好。
即使您确实设法找到了您想要的事件/方法/消息,您仍然需要能够处理断电的情况。
稍微有用一点,您是否尝试过禁用主变形直到准备好了?您可以将所有控件放在面板上,然后执行
panel1.enabled := false;
答案 5 :(得分:0)
感谢大家的帮助和建议。
以下是我使用的解决方案: 在'网格形式'(例如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
原来是一个简单的解决方案,只需要一段时间才能得到它。
似乎工作正常,但如果它有问题,那么我将纳入发送消息的想法。