在Delphi 6中防止表单停用

时间:2009-10-05 10:55:50

标签: delphi forms event-handling message

我们有一个Delphi 6应用程序,它使用非模态形式和网格编辑。在FormClose事件中,我们检查条目是否为正方形,如果不是则阻止关闭。

然而,如果用户点击后面的主表单,那么原始表单就会消失(正如您所期望的那样),但这允许用户移动到主屏幕上的新记录,而不会在网格中进行更改已经过验证。

我已经尝试过FormDeactivate事件,它会触发但似乎没有任何机制来阻止停用(与FormClose事件Action参数不同)。 我从网格中尝试了OnExit,但是在停用时它不会触发。 我试图捕获WM_ACTIVATE消息并设置Msg.Result = 1但这没有效果(可能是因为另一个WM_ACTIVATE消息正在发送到主窗体?)。

所以,我正在寻找关于如何(有条件地)阻止用户点击另一个表单时停用表单的想法。 (PS我不想将表单样式更改为fsStayOnTop)

由于

6 个答案:

答案 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  

原来是一个简单的解决方案,只需要一段时间才能得到它。

似乎工作正常,但如果它有问题,那么我将纳入发送消息的想法。