【问题标题】:Delphi: How to use windowless controls?Delphi:如何使用无窗口控件?
【发布时间】:2011-01-19 22:18:48
【问题描述】:

我知道windowless controls are not magic。无窗口控件可以具有输入焦点(例如 Internet Explorer)。输入焦点也无非是绘图:

当用户开始混合键时,做出适当的反应。你知道击键是为了那个有焦点的控件,因为那个控件有焦点。

对于我的 (Windows®) 窗口,我必须知道我的无窗口子控件(让我们假装它是 TGraphicControl 的后代)获取键盘事件。所以在我的表单OnKeyDownOnCharOnKeyUp 中,我需要假装他们要去我的无窗口子控件。

我能做到,但很痛苦。

但随后用户可能会想要使用 Tab 导航,我将不得不以某种方式拦截 Delphi 的正常选项卡控制顺序处理,并让自己说 这件事 是 Tab 键顺序中的下一个(和上一个)。

我能做到,但很痛苦。

然后是ActiveControl,除了TWinControl,它什么都不懂。因此,如果 Delphi 试图弄清楚谁有焦点,那它会发疯的。所以我必须有一个 ActiveControl 的替代实现。

我能做到,但很痛苦。

换句话说:这是不是工作量太大了?我正在与 Delphi 的所有东西作斗争,所以我可以通过键盘输入访问几十个无窗口控件? Delphi 的设计者从来没有考虑过使用交互式无窗口控件,如果我现在尝试使用它,我会卡在hurtlocker

Delphi给了我心甘情愿帮助我的机会,但我选择了痛苦的方式。


需要对无窗口控件做一些进一步的解释。

并非您与之交互的每个控件都必须是 Windows 控件。很有可能将焦点放在非 Windows 窗口的控件上并将键盘输入发送到该控件。

例如,您在 Internet Explorer 浏览器窗口中看到的几乎每个控件都是无窗口控件。在以下屏幕截图中,您可以看到一个可以输入的 edit 控件,以及一个(在此屏幕截图中)具有焦点的按钮:

您可以看到虚线的焦点矩形,并且按钮是蓝色的(在 Windows 上表示它有焦点)。

如果我在Google Search 按钮获得焦点时按空格键,它将按钮。之所以可行,是因为 Microsoft 编写了一个完整的控件小部件库。这些控件的外观和感觉(几乎)与常规常用控件完全一样——它们几乎是 Windows 常用控件的完全复制品,直至应用的主题。

Mozilla Firefox 和 Google Chrome 也使用控件的小部件库。它们不使用 Microsoft 的内置窗口控件,而是使用图形化、交互式、无窗口 widgets 库。

如果您有合适的开发环境,那么无窗口小部件就像“普通”窗口控件一样工作。 GTK+ 是一个小部件库,而 Glade 是一个 IDE,可让您在该小部件库中布局控件。

我不知道 Firefox、Chrome 或 Blender 是在什么开发环境中创建的,但它们的小​​部件支持无窗口控件。


那么现在来回答我的问题。

除非我弄错了,在我看来,虽然 Delphi 支持基础TControl,(它具有宽度、高度,并且可以自己绘制),但它无法接收键盘焦点。在我看来,Borland 从未将 Delphi 的 VCL 设计为通用的小部件库。我必须支持这一点的唯一证据是表单的 ActiveControl 是 TWinControl:

property ActiveControl: TWinControl;

这并不意味着 Delphi 可以或必须仅限于窗口控件。可以扩展 VCL 小部件库以支持将焦点放在无窗口控件上。

但也许 Delphi 已经支持无窗口控件,我只是没有意识到? Delphi 中是否已经建立了支持关注TControl 的机制?但我是一个相当聪明的人,而且我很确定 Delphi 的 VCL 无法做到其他小部件库可以做到的事情。

这又引出了另一个问题:子类化表单需要做多少工作才能支持它?那里还有其他人,也许是 TeamB 中的某个人,比我聪明得多,谁已经尝试过了,并得出了不可能的结论?

我现在先问一下,如果尝试添加无窗口控件支持几乎是不可能的(即徒劳无功) - 所以我不会白白花费数周时间。我正在尝试利用 Delphi 开发人员社区的知识。

我在问一个问题。

【问题讨论】:

  • 投票结束为“不是一个真正的问题”。一旦你去掉了装饰,真的没有。 @Ian,SO 是一个问答网站,它试图帮助您(以及以后有类似问题的其他人)获得对 real 问题的良好答案。如果你不知道自己为什么要做这一切,其他人应该怎么做?
  • Delphi 是否有支持无窗口控件的 Widget 库(无论是 Borland 的 VCL 还是其他库)?或者,窗口控件的概念是否深入到 Delphi 中,以至于 a) 不仅不存在,而且 b) 如果不丢弃 Delphi IDE 所提供的几乎所有东西,几乎不可能创建一个?

标签: delphi windowless


【解决方案1】:

构建无窗口控件将它们装入 Delphi 的 VCL 框架是徒劳的。

您以 Internet Explorer 为例。但在这种情况下,它完全负责驻留在上面的一切。它对主动控件是什么有自己的内部概念,但想想它从外部看起来是什么样子:它只是一个巨大的控件。当您询问操作系统什么具有焦点时,单个浏览器控件具有它,无论浏览器的哪个子控件似乎具有焦点。

当你按下 Tab 时,它看起来就像浏览器只是简单地使用了一个制表符,就像编辑控件一样。编辑控件将光标移动到几个空格上并将制表符添加到其内部缓冲区;浏览器控件将光标移动到显示的另一个区域。

您正在考虑在 Delphi TForm 上执行所有这些操作。 Delphi 表单已经有一个管理活动控件和处理击键的框架,您将不得不与这一切作斗争。如果您想要无窗口控件,请使用 Internet Explorer 路线并构建您自己的容器控件来保存它们,这样您就可以继续掌控其中发生的一切。

你的容器可以是一个 VCL 控件,但你放在它上面的东西可能不能——他们仍然期望使用 VCL 焦点和键盘处理规则。请注意,您也不能将普通的 Windows 控件放入 Internet Explorer。你放在那里的任何东西都需要通过特定的 ActiveX 接口。也许您也需要接口,或者您可以创建自己的一组控制类,这些控制类源自您设计用于与容器一起使用的某些特殊祖先类。不要以TGraphicControl开头;它在 VCL 中根深蒂固,无法用作分支控件库的基础。

这将是很多工作,但话说回来,Internet Explorer 也是如此。

【讨论】:

  • “Delphi 表单已经有一个管理活动控件和处理击键的框架,你将不得不全力以赴。”谢谢你。这真的是我所希望的。 Delphi 是从头开始编写的,没有无窗口控件。如果我要尝试一些编程来创建一个无窗口控件:我会在每一步都与 Delphi 作斗争。
  • 此外,Microsoft 没有将一个控件转换为无窗口版本 - 组合框。您会注意到组合框总是出现在任何其他 html 内容的“顶部”——无论您使用多少 z-orders。那是因为它仍然是一个 Windows 窗口控件。
  • 我创建了一些控件。我希望它们没有窗户,所以它们会“轻量级”,也因为它允许它们部分透明。但是当需要 TAB 导航到它们时,我意识到 TControls 不是交互式的。尽管它们会响应发送给它们的键盘事件,但我必须让 Delphi 相信它们是值得关注的控件。
  • 要做你想做的事,你需要为你的控件提供一个窗口容器。这并不妨碍您将其他窗口控件放入容器中;他们只需要知道如何与您的容器合作。 Internet Explorer 的组合框也不例外。它有一个窗口句柄,但它仍然是专门为 IE 设计的。它不是标准的 Windows 组合框。 IE 提供了 ActiveX 接口,因此您可以调整其他控件以适应 IE。
  • 我猜最后一个问题是:是否可以将 Delphi 用于无窗口控件?假设我编写(或导入)每个控件(按钮、编辑框、复选框、树视图、listivew、状态栏等)的无窗口版本。进一步假设我创建了一个 TForm 的后代,它确实理解子 TControl's:TApplication 不是太习惯于 TForm 的想法吗?我注意到TScreen 也有一个ActiveControl: TWinControl,我是否必须创建新版本的 TApplication 和 TScreen 以及其他所有内容?换句话说……没用?
【解决方案2】:

是的,这是徒劳的。
这不是 Delphi 的错,你只是在与 Windows 本身作斗争。
如果您需要一个行为类似于窗口控件的控件,请使用窗口控件。
你是对的,试图从头开始重新创建窗口控件的整个 API 堆栈是一种痛苦。

【讨论】:

  • 其他成功的应用程序;他们是在与 Windows 本身作斗争吗?如果您使用的小部件库支持无窗口控件,则可以这样做。 Delphi 似乎从每个控件都必须窗口化的概念开始。我不知道 Internet Explorer、Chrome、Firefox 和 Blender 是用什么语言编写的,但显然是在一个小部件库支持无窗口控件的环境中。 Delphi 是否有机制支持这种无窗口控件(又名小部件)?
  • Ian,您误以为这些程序使用的是现成的小部件库。这些程序有它们自己的库,专为这项任务设计。
  • @Rob。那可能是,但是存在的小部件库。 GTK+、wxWidgets、Juce、Qt、Swing。还有其他库:WinForms、MFC。 VCL 适合前者(无窗口)还是后者(有窗口)?
  • 您确定 GTK+ 和 wxWidgets 库不使用窗口句柄吗?我怀疑他们会。不过,我不知道 Java 控件。它们可能不会,因为它们总是存在于 Java 容器中。所以,我们再次看到无窗口控件需要一个特殊用途的容器。
  • @Ian:Windows 上的 wxWidgets 可以使用窗口句柄(编译为 wxMSW)但不需要这样做(编译为 wxUniversal 时)。如果没有,那么框架必须实现焦点管理。但是原生的外观和感觉只能通过使用原生的窗口控件来实现 100%,所以 wxUniversal 永远是次要的选择。所以 wxWidgets 应该归入后一类。
【解决方案3】:

是的,你几乎已经想通了。使用无窗口控件意味着您失去了 Windows 可以帮助您的一切。在一个实际的窗口上拥有超过一对是很痛苦的。

【讨论】:

  • 我会失去 Windows 可以为我做的事情,但是那里有框架和小部件库可以做到这一点。 Delphi 是那些小部件库之一吗?是否可以要求 Delphi 支持非窗口控件?
  • 所有这些小部件库要么使用窗口控件,要么使用单个窗口控件自行完成所有繁重的工作。
【解决方案4】:

这些程序中的大多数最初很可能不是使用 RAD 类型的工具开发的,因此别无选择,只能重新发明轮子。 Delphi 的最大优势之一是深度 VCL 和 3rd 方组件支持,可提供您想要的外观。

我成功使用的一项技术是在基于复杂(报税)表单的应用程序中减少使用的窗口句柄数量,该技术是在画布上绘制文本,并将单个 TCustomEdit 后代移动到用户的位置正在编辑。捕获 TAB/Up/Down 键并将编辑移动到适当的位置是微不足道的。我们发现的挑战是在鼠标悬停的区域周围绘制一个热矩形。我们最终得到了一个 TObject 的网格数组,其中数组元素将是 nil(无字段)、一个 TLIst(网格包含多个字段)或一个包含我们的字段描述符的类。这减少了我们必须执行的范围检查的数量,因为该框更有可能只包含一个字段,或者最多包含 4 个字段。

【讨论】:

  • 哇——我们是在开发同一个软件,还是……? :-)
【解决方案5】:

fpGUI Toolkit 是您想要的示例。源代码库中最新的 fpGUI 代码基于多窗口设计。这简单意味着每个小部件/组件都有一个窗口句柄,但 Windows 或 Linux 对该窗口不做任何事情,除了基本的通知消息(mouseenter、mouseexit 等)。 fpGUI 仍然可以完全控制每个组件的位置,如果它们是可聚焦的,它们的外观等。fpGUI 中的一些小部件/组件也是非窗口组件。 eg:TfpgScrollbar、TfpgMainMenu、ComboBox 中的按钮等

如果您想要一个真正的非窗口版本,则意味着只有一个顶级窗口具有窗口句柄,该窗口内的所有其他小部件/组件实际上并不存在于操作系统中(它们没有窗口句柄),那么 fpGUI 也可以提供帮助。 fpGUI Toolkit 最初的设计就是基于这样的设计。同样,在源代码存储库中查找 v0.4 代码分支。在那个设计中,fpGUI 必须处理所有事情,创建 mouseenter/mouseleave 事件,转换容器组件的坐标系统,处理(假)组件焦点状态等......是的,最初的设计是很多工作,但是你有一个非常便携的框架,也可以很容易地应用于其他操作系统。

是的,fpGUI 完全用 Object Pascal 语言实现,使用 Free Pascal 编译器为我提供跨平台支持。目前 fpGUI 可在 Windows、Linux(32 位和 64 位)、Windows Mobile 和嵌入式 Linux (ARM) 设备上运行。

【讨论】:

  • 我认为对 OP 来说最重要的是链接页面中的“简介”部分,您可以在其中解释 为什么 放弃 该设计用于每个小部件一个句柄的设计。
  • 链接似乎已损坏。我猜wiki.lazarus.freepascal.org/fpGUI 是指向同一软件的更永久链接。还有fpgui.sourceforge.net
【解决方案6】:

我不知道你的问题到底是什么,但我认为这段小历史可能是相关的......

我们有一个填写十几个表格的应用程序。用户可以填写其他表格,也可以更改应用程序自己填写的值。

现在,在我们的第一个实现中,我们为每个输入字段使用了窗口组件,以便字段可以接收焦点和输入。结果证明这是个大问题,因为所有这些窗口都占用了大量资源。

我们现在对每个输入字段都有无窗口控件。这意味着我们最终得到的只是表单及其输入字段的组合图。当用户在绘图内部单击,或使用一些击键来移动/设置焦点时,我们为单击的字段创建一个新的窗口控件。当用户移动到下一个输入字段时,我们销毁第一个窗口,并创建一个新窗口。这样我们就只有一个窗口控件,这又给了我们很好的速度提升。

再说一遍 - 我不知道你真正想要管理什么。 TWinControl 是一个 TWinControl 是有原因的,但可能有一个解决方案来满足您的需求,无论是什么...

【讨论】:

  • 查看您在浏览器中输入的文本框。该文本框不是 Windows 窗口。它没有自己的窗口句柄或窗口过程。击键 发送到 Windows 窗口,但它们发送到的窗口是整个页面。您不需要 Windows 类来创建控件,您只需要一个不基于 Windows 类的控件库。 ie 团队将所有常用控件重新创建为无窗口版本(文本框、按钮、复选框等)。这些不是 Windows 控件,它们是无窗口的。
  • 好吧,你可以在 Delphi 中做任何你想做的事,但使用 Delphi 的部分目的是获得 VCL 提供的领先优势。可以在 delphi 中使用其他小部件库,有些甚至允许您使用 delphi ide 的部分功能,如调色板和检查器(例如twinforms.com/products/wxformsdelphi/index.php)。
【解决方案7】:

我认为fgGUI 可以帮到你。

请先检查其Wiki

我认为你可以在 Delphi 中为你的应用程序使用这个框架,因为它完全是用 Pascal 编写的。实际上它基于 FreePascal ;)

HTH

【讨论】:

猜你喜欢
  • 2012-10-14
  • 1970-01-01
  • 2011-01-21
  • 2012-06-07
  • 2012-06-23
  • 1970-01-01
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多