【问题标题】:Delphi TPageControl not responding to clicks on tabsDelphi TPageControl 没有响应点击选项卡
【发布时间】:2010-07-13 23:33:30
【问题描述】:

我在主窗体上有一个带有 TPageControl 的应用程序。 pagecontrol 有几个选项卡。该应用程序可以最小化为托盘图标。有时在运行最小化一段时间后,当我恢复主窗口(通过鼠标右键单击托盘图标)时,会显示最后显示的选项卡,但我无法选择任何其他选项卡!

如果我单击另一个选项卡,外观会发生变化,因此该选项卡看起来是活动选项卡(即选项卡本身移动到选项卡行的前面),但选项卡的主体保持不变。我也有菜单项和快捷键来选择其他选项卡,它们的行为相同。如果我键入 Alt-O(选项),顶部的选项选项卡将变为活动状态,但我看不到该选项卡的正文 - 我仍然看到其他选项卡的内容。

我已验证,当我单击另一个选项卡时焦点会从第一个选项卡上移开,而当我单击该选项卡时焦点会移回。

我尚未确定该行为是否仅限于特定选项卡,因为它需要一段时间才能发生。

有什么想法吗?

更新

有趣的笔记。我已经确定问题是在这些情况下发生的。应用程序启动,然后最小化到托盘。检测到警报条件,弹出一个窗口并恢复主窗口(这是应用程序的预期行为)。正是在这一点上观察到了故障 - 即,当我单击其他选项卡时,我看不到它们。

  • 启动应用程序。显示选项卡 1
  • 最小化应用程序。到托盘
  • 等待弹出窗口显示,主窗体已恢复
  • 点击 Tab 2 FAULT OBSERVED(Tab 2 body 不显示)
  • 在 TWinControl.CreateHandle 中放置断点
  • 点击 Tab 3 - 中断
  • 运行 - 不显示 Tab 3 正文
  • 单击选项卡 1 - 不会中断
  • 点击 Tab 3 - 不会中断
  • 单击选项卡 4 - 中断
  • 运行 - 不显示 Tab 4 正文
  • 点击 Tab 1、2、3、4 - 不会中断

因此,标签似乎在第一次被点击时创建了它们的句柄,从那时起,它们认为它们存在,但它们不显示。如果弹出窗口被禁用,则不会观察到故障。弹出窗口由 Application.OnIdle 任务触发。

另一个更新:一些进展。在网上四处逛逛后,我做了一些更改。

我删除了以下代码:

procedure RestoreMainWindow ;

begin
MainForm.WindowState := wsNormal ;
MainForm.visible := true ;
Application.Restore ;
Application.BringToFront ;
ShowWindow (Application.Handle, SW_SHOW) ;  { show the taskbar button }
end ;

并将其替换为:

procedure RestoreMainWindow ;

begin
MainForm.Show () ;
MainForm.WindowState := wsNormal ;
Application.BringToFront () ;
ShowWindow (Application.Handle, SW_SHOW) ;  { show the taskbar button }
end ;

我删除了:

procedure TTADMainForm.SendToTray (Sender: TObject) ;

begin
MainForm.visible := false ;
ShowWindow (Application.Handle, SW_HIDE) ;  { hide the taskbar button }
end ;
...
Application.OnMinimize := SendToTray ;    

并将其替换为:

procedure TTADMainForm.ApplicationEvents1Minimize(Sender: TObject) ;

begin
Hide();
WindowState := wsMinimized ;
TrayIcon1.Visible := True;
end ;

问题似乎已经消失了。然而。现在我可以在启动后最小化应用程序,弹出窗口并以模态方式显示,主窗体显示,所有选项卡显示和工作。但。我无法再次最小化表单。 OnMinimize 处理程序在第一次之后不会被触发。呸呸呸。

我仍然无法理解为什么它现在可以工作,这有点令人担忧。以及如何让它再次最小化??

【问题讨论】:

  • 以防万一它有助于什么版本的Delphi?

标签: delphi focus tabs click tpagecontrol


【解决方案1】:

完全从 5 年前的记忆开始工作,但这里是:

TPageControl 为其中的每个页面使用不同的窗口句柄。标签栏是它自己的窗口句柄,TPageControl 负责监听标签的变化并做出相应的页面隐藏/显示。所以,当你点击一个标签,标签跳转到包的最前面时,TPageControl应该隐藏当前页面窗口,并显示与所选标签对应的页面窗口。

通常,VCL 控件在实际需要时才会创建它们的窗口句柄 - 例如,当它实际显示时。这减少了窗口句柄消耗。在 Windows 3.1 和 Win95 中至关重要,但在当今基于 NT 的 32 位操作系统中并不那么重要。

为了最大限度地减少资源负载和启动时间,TPageControl 在创建控件时不会为其所有隐藏页面创建窗口句柄。页面窗口句柄将在首次显示时创建。

单击选项卡时页面未绘制的原因有几种可能:

  1. 耗尽 GDI 窗口句柄池。除非您使用的是 16 位 Windows 操作系统,否则极不可能。 (Win 3.1 或 Win95)
  2. 内存泄漏导致您的应用程序溢出到交换文件中并破坏硬盘。该应用程序将几乎停止运行,并且看起来像是被冻结了,并且不时出现 UI 活动的打嗝。
  3. 在没有消息循环的后台线程上创建窗口句柄。你在后台线程中做任何事情吗?在后台线程中触摸 VCL 控件可能会导致窗口句柄过早创建,并且窗口句柄将绑定到创建它的线程。如果该线程没有消息循环,则该窗口句柄将永远不会收到任何消息,因此它永远不会在屏幕上绘制自己。

没有。 3是你最有可能的罪魁祸首。那么,你在那个后台线程中做什么? ;>

【讨论】:

  • 感谢您的详细回答。没有明显的抖动,并且在事件发生后,就我所看到的控件而言,应用程序的行为完全符合我的预期。有一个后台线程 - 但我不相信它涉及 VCL。您是否希望控件上的所有页面(有 7 个)都变得无响应?如果后台线程场景中的窗口句柄是cuplrit,紧急解决方法是在启动时以编程方式依次选择每个页面吗?
  • 您需要检查文档或 TPageControl 的 VCL 源代码,以查看它在隐藏页面时是否会破坏页面窗口句柄。我不记得有没有。可能在 Win3.1 时代有,但在 Win32 中不再需要。
  • 如果隐藏页面时它不破坏窗口句柄,那么是的,在启动时触摸所有页面句柄将强制创建所有句柄。但是,这里要小心,因为如果您有十几个标签页并且每个页面上有 20 个控件,那么创建所有这些句柄将花费大量时间。这就是我们将 TPageControl 更改为延迟加载的原因——Delphi IDE 的选项对话框加载时间过长! ;>
  • 仔细查看后台线程如何与 UI 或前台线程交互。如果它调用一个函数,该函数调用一个函数,该函数调用一个将字符串分配给 Button.Caption 的函数,这会在后台线程的上下文中发生,并且可以强制 Button 控件立即创建其窗口句柄。如果线程之间的唯一通信是通过 PostMessage 或在共享数据结构中设置字段,那么您的后台线程是完全隔离的。
  • 在 TWinControl.CreateHandle 或类似函数中设置断点 - 这是窗口句柄创建步骤的一部分。您可能希望先启动您的应用程序,然后设置断点,因为这将是一个非常繁忙的代码区域。运行你的后台线程,看看你在创建句柄中的断点是否被命中。如果是这样,请查看寄存器窗口或线程窗口以查看当前线程句柄是什么,并将其与您的后台线程进行比较。如果匹配,请查看调用堆栈以了解您是如何从后台线程到达那里的。
猜你喜欢
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
  • 2016-09-22
  • 2020-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多