【问题标题】:Can Windows manage multiple focus points on a single control?Windows 可以在单个控件上管理多个焦点吗?
【发布时间】:2012-01-03 17:21:55
【问题描述】:

我正在构建一个具有多个焦点的自定义控件。例如,在 1 个控件内,假设有 3 个区域(可以定义为画布上的一个矩形)能够获得焦点。根据哪个有焦点,我不希望默认的 Windows 虚线围绕它,而是一些特殊的处理。我什至不知道如何让 1 个自定义控件拥有自己的焦点。

最初的项目是一个单独的 TPanel,上面有几个 VCL 控件,每个控件当然都是自己的窗口,因此有自己的焦点。但现在我将它放入自己的自定义类中,这 3 个控件将不再存在(它们仅在版本 1 中作为原型存在),我现在需要以某种方式模仿这些不同区域的焦点。

我想类似于像 TListBox 这样简单的东西,该控件中的某些项目获得焦点,而不是整个控件本身。

这里有一张图片来帮助展示我正在制作的东西......

顶部的那个是带有按钮的原件。但底部的那个是我正在建造的新的,它都是自定义绘制的。

为了详细说明,我想在重新发明轮子之前看看 Windows 是否已经对这种类型的场景进行了特殊处理。

我不是在寻找“最简单”的方法来实现这一点。而且我也不希望建议恢复到我以前的方式,因为有很多原因我不希望 1 个控件与多个其他控件一起使用。我只需要一个是或否,以及一个解释。

更多

我刚刚意识到主要问题是tab 键的使用。控件必须首先获得焦点,开始关注它应该关注的任何子项,然后在我的命令上响应tab,直到它到达末尾,然后将制表符传递给下一个控件。然后它也需要 shift+tab 。我到底将如何实现这个选项卡?这就是我卡住的地方,但我突然意识到这是我问的主要原因。

【问题讨论】:

  • 列表项没有获得焦点(就像键盘焦点的意义),它是有焦点的框。如果是这种情况,您将无法通过按项目的第一个字符来导航项目。项目仅具有被聚焦/选择的视觉指示。不过,您当然可以模拟行为..
  • @SertacAkyuz 那么对于这种情况没有额外的 Windows 处理?我想会是这样,但我不确定 Windows 中是否已经内置了一些特殊的处理例程。
  • 不,就操作系统而言,窗口是可以接收键盘焦点的,取决于您如何对其进行分区。不确定它是否与您的问题完全相关,但请参阅“Keyboard Focus and Activation”。
  • 我不太确定,但您可以前往here 并在法语大页面中搜索“Navigateur hyperbolique”。它是关于采用焦点和上下文概念的 delphi 组件。
  • 我认为是时候提出一个新问题了

标签: delphi focus custom-controls delphi-7


【解决方案1】:

关于 TAB 键的处理 - 它应该是这样的:您处理 WM_GETDLGCODE message 以表明您要处理 TAB 键,即

TMyControl = ...
  protected
     procedure WMGetDlgCode(var Msg: TMessage); message WM_GETDLGCODE;
     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
...
procedure TMyControl.WMGetDlgCode(var Msg: TMessage);
begin
  inherited;
  Msg.Result:= Msg.Result or DLGC_WANTTAB;
end;

在被覆盖的KeyDown 方法中,您决定如何响应它,例如

procedure TMyControl.KeyDown(var Key: Word; Shift: TShiftState);
begin
  if(Key = VK_TAB)then begin
     if(ssShift in Shift)then begin
        if(first subcontrol is focused) set focus to previous control on parent
        else set focus to previous child area
     end else begin
        if(last subcontrol is focused) set focus to next control on parent
        else set focus to next child area
     end;
  end else inherited;
end;

【讨论】:

  • +1 很棒的东西,虽然我实际上只是想问另一个关于标签的问题,但是谢谢,很大的帮助。
  • 这将是一个关于这个确切问题的问题的更好答案。 “如何向自定义控件添加选项卡处理”
【解决方案2】:

不,您不能让窗口识别同一个窗口句柄内的多个键盘焦点,因为每个具有窗口句柄的控件都具有或不具有键盘焦点。多个控件之间的“内焦”,由你来梳理。

如您所知,实现此目的最简单的方法是拥有多个带有自己的窗口句柄的子控件,这就是您所说的:

TMyThreeEditControls = class(TControl) //  parent has no window handle!!!!
    protected
        FEdit1:TEdit;
        FEdit2:TEdit;
        FEdit3:TEdit;
    ...
end

在上面的情况下,父控件是一个TControl,它创建了几个子控件,在我上面的例子中,三个都有自己的窗口句柄,因此Windows可以在你点击Tab时显示键盘焦点,并处理鼠标焦点作为 Windows 通用控件库功能的一部分。

简而言之,在主控件中包含子对象(其他控件)的“复合”方法实际上是让 Windows 完成大部分工作的唯一方法。

另一方面,您可能正在寻找的不是一种让控件绘制本身的方法,而是在您自己的自定义绘制例程中使用一些代码来使某些东西看起来像是聚焦的,如果您就是这样的话寻找,您应该查看 VCL 源代码或 about.com 上的 link,以获取有关如何告诉 Windows 绘制焦点矩形等的示例。 about.com 链接是模仿的,不使用真正的 Windows 代码以 Windows 主题感知方式吸引焦点。

更新:您可能还在寻找确定鼠标坐标是否在指定矩形内的方法(在您的情况下,矩形代表一个按钮),如果是,则绘制“热状态” " 为按钮。如果您想自己构建一个控件,还有更多的子任务需要完成,我建议您研究VCL源代码中现有的控件,例如TStringGrid和TCategoryButtons,以查看您需要的MouseMove、MouseDown和MouseUp处理代码去做你想做的事情。特别是,StringGrid 源代码是查看如何在具有单个窗口句柄的单个控件中使用“tab 键”的方法,因为在该控件中可以使用 tab 键(如果打开了正确的选项)来在字符串网格内的所有单元格之间导航,就好像每个单元格都是一个单独的控件一样,即使它都是一个控件。

【讨论】:

  • 确实这可能是最简单的方法,但我不是在寻找简单(不是我在寻找困难)而是我只是想看看 Windows 是否已经有这样的一个内置的功能 - 但看起来答案正在展开。这正是我构建版本 1 的方式,现在我正试图摆脱多个控件。
  • 当然,它具有“内置”的所有功能。它被称为 Windows 通用控件。你的问题没有意义。您似乎要问的是“它是否内置,我可以在不使用多个控件的情况下使用它”。在这种情况下,答案是“绘制代码是内置为您可以调用以绘制焦点矩形的方法”,但现在跟踪谁有焦点是您的工作。
  • 我在您完成回答之前发布了该消息 - 第二部分是肯定的 - 我想要 1 个油漆程序、1 个手柄,并模拟不同区域的焦点。感谢您的链接:D
  • 我非常清楚 Windows 已经在其控件中内置了焦点。我想知道 Windows 是否支持“具有多个焦点的 1 个控件”。
  • 如果你写了一个控件,Jerry,那么 Windows 不知道你写过这样的东西。如果您不使用通用控件,那么您将自己接管管理绘画、消息处理、焦点处理、绘图等的整个工作。 Delphi VCL 和 Windows API 中有许多功能可以使这更容易,但最终需要您进行大量的协调和细心的工作,而我所知道的任何方式都无法自动化。有关执行您所说的所有操作的控件示例,请查看 VCL 中的 TStringGrid 源代码。
【解决方案3】:

实现此目的的另一种方法是使用一个编辑框,该编辑框可重复用于您想要“聚焦”的每个区域。这基本上就是 Delphi 的网格的工作方式。

当用户在该区域单击(或在控件中按 Tab 键)时,您将编辑控件文本设置为该区域中的数据,将编辑控件边界设置为该区域并使其可见。当用户退出控件时(通过单击或跳出),您将隐藏编辑控件。如果你让你的控件接受 tab 键,你可以让它在他们点击 tab 时“编辑”下一个区域,当他们在最后一个区域时退出它。

然后,它只是确保您将输入的数据存储在组件中的正确位置。

【讨论】:

  • 感谢您的建议,实际上我以前在网格上使用过组合框,我不需要创建任何 VCL 控件。它本质上只是具有特殊处理的按钮,我可以通过使用 Hit Test 为绘图和鼠标事件保留控件画布的区域来轻松实现这一点。当我想把重点放在这些地区之一时,麻烦就来了。
  • TCanvas.DrawFocusRect 是你的朋友。 :) 跟踪哪个区域被聚焦,并在绘制控件时绘制焦点矩形。
  • 其实我的部分目标是对整个应用程序进行皮肤处理并消除焦点虚线,但这是一个完全不同的主题。我将自己的注意力集中在控件上,因为它们并非都是矩形的。 +1 的好提示虽然
猜你喜欢
  • 1970-01-01
  • 2010-12-13
  • 2019-06-04
  • 1970-01-01
  • 2021-05-19
  • 2017-05-22
  • 2021-03-18
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多