【问题标题】:Prevent Form Deactivate in Delphi 6在 Delphi 6 中防止表单停用
【发布时间】:2010-12-03 21:55:02
【问题描述】:

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

但是,如果用户单击后面的主表单,则原始表单会消失在后面(如您所料),但这允许用户移动到主屏幕上的新记录,而不会在网格中进行更改已验证。

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

因此,我正在寻找有关如何(有条件地)防止在用户单击另一个表单时停用表单的想法。 (PS我不想把表单样式改成fsStayOnTop)

谢谢

【问题讨论】:

  • 表单是非模态的有什么原因吗?因为听起来你想要一个模态表单。
  • 这也是我的问题之一,但显然它不能是模态的;但在验证数据之前,它需要表现得好像是模态的!
  • 好的,这是一个一直打开的表单。如果无法验证数据,则它充当模态表单,但如果可以验证数据,则表单不会关闭?
  • 如果无法验证数据,则用户应该无法点击离开(停用)此表单。如果确定,那么他们可以单击主窗体并选择要显示的另一条记录。 “网格形式”只有一个实例。一个选项卡允许输入总价,另一个选项卡允许输入总计的组成部分。如果他们没有平方,那么用户可以选择将总数更改为组件的总和,但是如果他们在组件网格上单击离开表单,我需要应用此检查。谢谢

标签: delphi forms event-handling message


【解决方案1】:

感谢大家的帮助和建议。

这是我采用的解决方案: 在 'Grid Form' (eg 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  

原来是一个简单的解决方案,只是花了一段时间才搞定。

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

【讨论】:

    【解决方案2】:

    您还可以在模型中引入一个状态,以跟踪您在此处描述的窗口是否需要焦点,并在其他窗体上使用 onFocus 处理程序以编程方式将焦点设置回网格窗口。

    [编辑]我的评论副本:

    您可以使用网格注册表单的 onShow 事件。 (如果您实现它,请务必使其以某种方式可配置,以最小化网格对应用程序当前布局的依赖。也许通过提供由表单调用的方法,该方法反过来触发调用表单中网格的事件注册用于 onShow 事件)

    详细说明事件注册:

    您可以以编程方式附加事件处理程序。网上有很多关于这个的方法。我这里没有可用的 Delphi,所以我现在无法复制工作代码。

    用于以编程方式附加事件的伪代码!

    myform.onShow=myGrid.formOnShowHandler;
    

    formOnShowHandler 与 IDE 为 onShow 事件生成的函数具有相同的签名。它有一个参数,您可以使用该参数来确定哪个表单调用了处理程序,这样您就可以重用该函数,只需将表单放在后台并再次显示您的网格表单(例如,它将是网格的父级)。

    【讨论】:

    • 感谢帕特里克的建议。我在一个测试应用程序中通过在我的网格表单(Form2)中添加一个公共布尔“编辑”来尝试这个,然后将它添加到主表单的 OnActivate 事件中:如果 Form2.Editing 然后 Form2.Show;它似乎有效,但我希望以网格形式控制它,使其保持整洁,并节省对主窗体的每个 FormActivate 的检查。
    • 您可以使用网格注册表单的onShow事件。 (如果您实现它,请务必使其以某种方式可配置,以最小化网格对应用程序当前布局的依赖。也许通过提供由表单调用的方法,该方法反过来触发调用表单中网格的事件注册对于 onShow 事件)
    • 不确定我是否关注你。您是否介意扩展它的工作方式以及如何注册活动。谢谢
    【解决方案3】:

    这不是一个有用的回答大卫,但我认为我必须同意其他受访者的观点,即这不是正确的方法。有很多方法都可能出错,所以停下来看看另一种方法来解决你的问题可能会更好。

    即使您确实设法找到了符合您要求的事件/方法/消息,您仍然需要能够处理断电的情况。

    在一个更有用的注释中,您是否尝试过禁用主窗体直到准备好?您可以将所有控件放在一个面板上,然后就可以了

    panel1.enabled := false;
    

    【讨论】:

    • 如果停电了,也不存在用户在编辑表单中使用不一致、未提交的数据来操作主表单的危险。
    【解决方案4】:

    Windows 中的一条经典规则是您不能在焦点更改事件期间更改焦点。 OnDeactivate 事件在焦点更改事件期间发生。您的表单被告知它正在被停用——操作系统没有请求许可——同时,另一个表单被告知它正在被激活。两个窗口在这件事上都没有任何发言权,在这些事件发生时试图改变焦点只会让所有的窗口感到困惑。症状包括让两个窗口自己绘制,就好像它们有焦点一样,并且尽管输入光标闪烁,但键盘消息却无处可去。 MSDN 更可怕,虽然我从未目睹过这么糟糕的事情:

    在处理此消息 [WM_KILLFOCUS] 时,不要进行任何显示或激活窗口的函数调用。这会导致线程让出控制并可能导致应用程序停止响应消息。如需更多信息,请参阅Message Deadlocks

    由于您不能在焦点更改已经开始后拒绝它,因此要做的就是延迟处理事件,直到事情稳定下来。当您的编辑表单被停用且其中的数据无效时,发布表单一条消息。 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;
    

    我应该指出,这个答案在技术上并不能回答您的问题,因为它不会阻止表单停用,但您拒绝了确实可以防止停用的 my other answer

    【讨论】:

      【解决方案5】:

      当您致电ShowModal 时,除显示的表单外,所有表单都将被禁用。它们在 ShowModal 返回之前重新启用。

      显示您的编辑表单非模态,当数据开始被编辑时,通过禁用其他表单使表单自身成为模态。编辑完成后启用其他表单。显然,禁用窗口并不总是像设置Enabled 属性那么简单。我建议使用DisableTaskWindows,但它会禁用所有 窗口,包括您的编辑表单。尽管如此,看看它是如何在 Forms.pas 中实现的。它保留了所有它禁用的窗口的列表,以便之后只有它们才能重新启用。

      【讨论】:

      • 感谢罗布的建议。问题是组件价格是在网格中单独编辑的,因此只有在用户关闭表单时才能检查总价格(这就是我们所做的)。但是,当用户点击离开时,我们无法捕获(或更具体地阻止)FormDeactivate 事件。我想我可以显示一个新的模式表单来编辑价格然后验证它们,但我希望有一种方法可以防止表单被停用。也许不是?
      • 通过禁用“点击离开”的其他目标,您可以有效地阻止FormDeactivate 事件,至少在您的应用程序的上下文中是这样。 (用户可以点击转到另一个程序,但无论如何你都不应该阻止它——这是非常粗鲁的,并且可能会将业务发送给你的竞争对手。)我建议在编辑完成后启用另一个表单。如果只有在关闭编辑表单时才能完成编辑,那么从逻辑上讲,这意味着我建议在关闭编辑表单时启用另一个表单。有问题吗?
      【解决方案6】:

      Delphi 2006 引入了 OnMouseActivate 事件。主窗体的 OnMouseActivate 可以让您在其他窗体可见时阻止激活主窗体。

      这当然不适用于 D6。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-10
        • 2010-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多