pmAuto ModalPopupMode正确使用或错误解决方法 [英] pmAuto ModalPopupMode proper use or bug workaround

查看:179
本文介绍了pmAuto ModalPopupMode正确使用或错误解决方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用TApplication.ModalPopupMode = pmAuto时遇到问题,我想知道我的问题是由使用pmAuto还是Delphi中的错误引起的。

I'm having problems using TApplication.ModalPopupMode=pmAuto and I was wondering if my problems were caused by my usage of pmAuto or a bug in delphi.

简单用例:


  • Form1(MainForm)和Form3是永久性表单。 (在dpr中创建)

  • Form2在需要时创建,然后释放

  • Form3包含一个带有X个项目的TComboBox。 / li>
  • Form1(MainForm) and Form3 are permanent forms. (Created in the dpr)
  • Form2 is created when needed and freed afterward.
  • Form3 contains a TComboBox with X items.

动作顺序:


  • Form1创建

  • Form2显示form3模态。

  • 关闭Form3

  • 关闭并免费使用Form2

  • 显示Form3< ---- TComboBox现在包含0个项目。

  • Form1 create and show Form2 modal.
  • Form2 show form3 modal.
  • Close Form3
  • Close and free Form2
  • Show Form3 <---- The TComboBox now contains 0 items.

我使用以ComboBox为例,但是我猜想任何在DestroyWnd过程中保存信息并在CreateWnd过程中恢复信息的控件都无法正常工作。我测试了TListBox,它也显示了相同的行为。

I use ComboBox as an example, but I guess any controls that saves information in the DestroyWnd procedure and restore it in the CreateWnd procedure isn't working right. I tested TListBox and it displays the same behavior too.


  • 众所周知,ModalPopupMode时不应混合使用永久形式和临时形式是pmAuto吗?

  • 如果不是,是否有任何已知的解决此问题的方法?

  • 如果是错误,请在更新的版本中解决此问题吗?德尔福? (我正在使用XE4)

推荐答案

这并不是一个错误,只是一个怪癖

It is not really a bug, just a quirk in how the various windows interact with each other when dealing with modality.

第一次创建 Form3 时,<$在DFM流式传输期间调用c $ c> TComboBox.CreateWnd()。第一次调用 Form3.ShowModal()时, Form3 调用 RecreateWnd() 如果其 PopupMode pmNone Application.ModalPopupMode 不是 pmNone 。好的,因此调用 TComboBox.DestroyWnd(),保存项目,然后调用 TComboBox.CreateWnd(),恢复项目。在 ShowModal()期间重新创建 TComboBox 的窗口不是很理想,但是这次可以。

When Form3 is first created, TComboBox.CreateWnd() is called during DFM streaming. When Form3.ShowModal() is called for the first time, Form3 calls RecreateWnd() on itself if its PopupMode is pmNone and Application.ModalPopupMode is not pmNone. OK, so TComboBox.DestroyWnd() gets called, saving the items, then TComboBox.CreateWnd() gets called, restoring the items. Recreating the TComboBox's window during ShowModal() is not ideal, but it works this time.

第二次调用 Form3.ShowModal()时, TComboBox.CreateWnd()不调用c $ c>,而不会先前调用 TComboBox.DestroyWnd()!由于尚未保存项目,因此无法还原。这就是为什么 TComboBox 为空的原因。

When Form3.ShowModal() is called the second time, TComboBox.CreateWnd() is called again without a previous call to TComboBox.DestroyWnd()! Since the items have not been saved, they cannot be restored. That is why the TComboBox is empty.

但是为什么会这样呢?释放 Form2 时, Form3 的窗口仍与 Form2 的窗口。第一次调用 Form3.ShowModal Form2 的窗口设置为 Form3 的父/所有者窗口。当您关闭 TForm 时,它只是隐藏的,它的窗口仍然存在。因此,当 Form2 Form3 关闭时,它们仍然存在并链接在一起,然后当 Form2 被销毁,其所有子窗口和拥有的窗口也被销毁。 TComboBox 收到一条 WM_NCDESTROY 消息,将其 Handle 重置为0而不通知其其余代码该窗口已被破坏。因此, TComboBox 没有机会保存其当前项目,因为未调用 DestroyWnd() DestroyWnd()仅在VCL本身正在破坏窗口时调用,而不是在操作系统破坏窗口时调用。

But why does this happen? When Form2 is freed, Form3's window is still associated with Form2's window. The first call to Form3.ShowModal set Form2's window as Form3's parent/owner window. When you close a TForm, it is merely hidden, its window still exists. So, when Form2 and Form3 are closed, they still exist and are linked together, and then when Form2 is destroyed, all of its child and owned windows get destroyed. TComboBox receives a WM_NCDESTROY message, resetting its Handle to 0 without notifying the rest of its code that the window is being destroyed. Thus, TComboBox does not have a chance to save its current items because DestroyWnd() is not called. DestroyWnd() is called only when the VCL itself is destroying the window, not when the OS destroys it.

现在,如何解决这个问题?您必须销毁 TComboBox 的窗口,并触发其 DestroyWnd()方法,然后释放 Form2 。诀窍是,只有在<$中启用 csRecreating 标志时, TComboBox.DestroyWnd()才能保存项目。 c $ c> TComboBox.ControlState 属性。有几种不同的方法可以完成此操作:

Now, how can you fix this? You will have to destroy the TComboBox's window, triggering its DestroyWnd() method, before freeing Form2. The trick is that TComboBox.DestroyWnd() will save the items only if the csRecreating flag is enabled in the TComboBox.ControlState property. There are a few different ways you can accomplish that:


  1. 通话 TWinControl.UpdateRecreatingFlag() TWinControl.DestroyHandle()直接。它们都受保护,因此您可以使用访问器类来访问它们:

  1. call TWinControl.UpdateRecreatingFlag() and TWinControl.DestroyHandle() directly. They are both protected, so you can use an accessor class to reach them:

type
  TComboBoxAccess = class(TComboBox)
  end;

Form2 := TForm2.Create(nil);
try
  Form2.ShowModal;
finally
  with TComboBoxAccess(Form3.ComboBox1) do
  begin
    UpdateRecreatingFlag(True);
    DestroyHandle;
    UpdateRecreatingFlag(False);
  end;
  Frm.Free;
end;
Form3.ShowModal;


  • 调用 TWinControl.RecreateWnd()直接。它也是受保护的,因此您可以使用访问器类来访问它:

  • call TWinControl.RecreateWnd() directly. It is also protected, so you can use an accessor class to reach it:

    type
      TComboBoxAccess = class(TComboBox)
      end;
    
    Form2 := TForm2.Create(nil);
    try
      Form2.ShowModal;
    finally
      TComboBoxAccess(Form3.ComboBox1).RecreateWnd;
      Frm.Free;
    end;
    Form3.ShowModal;
    

    实际上不会创建 TComboBox 窗口直到下一次需要,在随后的 ShowModal()中。

    The TComboBox window is not actually be created until the next time it is needed, in the subsequent ShowModal().

    发送 TComboBox 窗口中显示一条 CM_DESTROYHANDLE 消息,并让 TWinControl 为您处理所有事情:

    send the TComboBox window a CM_DESTROYHANDLE message and let TWinControl handle everything for you:

    Form2 := TForm2.Create(nil);
    try
      Form2.ShowModal;
    finally
      if Form3.ComboBox1.HandleAllocated then
        SendMessage(Form3.ComboBox1.Handle, CM_DESTROYHANDLE, 1, 0);
      Frm.Free;
    end;
    Form3.ShowModal;
    

    CM_DESTROYHANDLE 由<$ c内部使用$ c> TWinControl.DestroyHandle()销毁子窗口时。当 TWinControl 组件收到该消息时,它将调用 UpdateRecreatingFlag() DestroyHandle()本身。

    CM_DESTROYHANDLE is used internally by TWinControl.DestroyHandle() when destroying child windows. When a TWinControl component receives that message, it calls UpdateRecreatingFlag() and DestroyHandle() on itself.

    这篇关于pmAuto ModalPopupMode正确使用或错误解决方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆