【问题标题】:TLabel displays accelerator keys even when the UI state says not to即使 UI 状态不允许,TLabel 也会显示加速键
【发布时间】:2011-08-02 12:56:16
【问题描述】:

在默认的 Windows 设置下,除非用户按下 ALT 键,否则加速键不会显示在对话框中。

Delphi的TLabel控件不遵守这个约定,如下图:

虽然标签和复选框都指定了加速键,但复选框正确地隐藏了它,但标签没有。当然,当ALT被按下时,加速器显示复选框,但这是不正确的行为。

我对为什么会发生这种情况的理解是,实现此行为的 VCL 代码包含在 TWinControl 中,例如 UpdateUIState 方法,并且依赖于向底层窗口控件发送 WM_CHANGEUISTATE 消息。由于TLabel 没有窗口化,它错过了这个处理。

任何人都可以提出一种方法来实现非窗口控件的所需行为吗?

更新 1

我刚刚发现组合框和无线电组也不响应 UI 状态。

更新 2

QC#97044.

【问题讨论】:

  • 我是否认为您希望将其用于任何非窗口控件,而不仅仅是 TLabels?
  • 您是否在父窗口收到 WM_CHANGEUISTATE 时手动尝试设置/清除 TLabel.ShowAccelChar? (显然是残酷的黑客攻击。)
  • @Marjan 原则上我认为所有非窗口控件都需要它。在实践中,我想不出任何带有我使用的不是标签的加速器文本的控件。
  • 因此您可以编写一个小的非可视组件,添加所有您希望启用该行为的控件,或者在表单中添加一个列表 (FDynamicLabels:TObjectList)。
  • 摆弄 ShowAccelChar 根本无济于事。在 ShowAccelChar=false 的情况下,TLabel 将显示“&”而不是下划线。

标签: delphi


【解决方案1】:

我想我已经找到了处理它的方法。

function HideAccelFlag(Control: TControl): Integer;
begin
  //ask the top level window about its UI state
  while Assigned(Control.Parent) do begin
    Control := Control.Parent;
  end;
  if (Control.Perform(WM_QUERYUISTATE, 0, 0) and UISF_HIDEACCEL)=UISF_HIDEACCEL then begin
    Result := DT_HIDEPREFIX;
  end else begin
    Result := 0;
  end;
end;

type
  TUIStateAwareLabel = class(TLabel)
  protected
    procedure DoDrawText(var Rect: TRect; Flags: Longint); override;
  end;

procedure TUIStateAwareLabel.DoDrawText(var Rect: TRect; Flags: Integer);
begin
  if ShowAccelChar then begin
    Flags := Flags or HideAccelFlag(Self);
  end;
  inherited;
end;

通过将表单流机制与TReader.OnFindComponentClass 挂钩,我确保始终创建TUIStateAwareLabel 而不是TLabel

处理TCustomGroupBox后代更麻烦。对于他们,我将TCustomGroupBox.Paint的源代码复制到我的后代中,并再次使用HideAccelFlag

下一个任务是将其写成 QC 报告。

【讨论】:

  • 整洁,请做 QC。我会投票给它。你永远不知道我们是否会走运并进入 XE2。
  • @Marjan XE2 已经完成了。今日大公告。 Mac OSX 和 64 位。我等不及了(对于 64 位!)
  • 哦,错过了那个公告,从今天早上开始就没有检查组等。是的,我和我们(= 在工作中)尤其不能很快拥有 64 位。试图将 90 GigaByte 模型填充到 2 Gig(有时是 3)的虚拟内存中并且仍然有一些喘息的空间变得越来越艰难......
  • 对于"将表单流机制与TReader.OnFindComponentClass"挂钩,请参见Replacing a component class in delphi
【解决方案2】:

您可以使用TStaticText 代替TLabel

来自文档页面:

当组件的加速键必须属于窗口控件时,使用 TStaticText 而不是 TLabel。

【讨论】:

  • +1 我也曾有过这样的想法,但如果我记得的话,切换时会有轻微的皱纹。它是否也不能自动调整大小?无论如何,它仍然留下了 group box 和朋友们!
  • @David:它会自动调整大小,但从 Delphi 2010 开始,它不会对标题进行垂直布局,也不会自动换行。它也没有这些TLabel 属性:EllipsisPositionGlowSizeTouch (TTouchManager)。在 Delphi 6 中它不能是透明的,但在 Delphi 2006 中已经可以了。
【解决方案3】:

抱歉没有代码,但也许是一个解决方案的方向。

我看到 TWinControl 使用 NotifyControls 向所有包含的控件广播消息。它用于通知控件Parent* 属性的变化,例如

procedure TWinControl.CMShowHintChanged(var Message: TMessage);
begin
  inherited;
  NotifyControls(CM_PARENTSHOWHINTCHANGED);
end;

我猜你可以做的是在你的表单上编写一个消息处理程序来获取 WM_CHANGEUITSTATE 并将它传递给表单的所有控件,使用 NotifyControls 或类似的东西,只将它传递给容器和 TLabels。

然后,您仍然需要一种方法让您的 TLabel 对该消息做出反应。我猜(尚未调查)您可以覆盖(后代或拦截器)WndProc 方法和/或对 FDefWndProc(受保护的属性)执行某些操作。

你将不得不在...中满足窗体中的框架

【讨论】:

  • 不幸的是,迭代和通知孩子是我已经知道该怎么做的事情。让我感到困惑的是加速器的实际抑制以及何时清除抑制的检测。
  • @David:我注意到你找到了标签的方法......赞成它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-14
  • 1970-01-01
  • 2014-04-05
  • 2020-02-03
  • 1970-01-01
  • 1970-01-01
  • 2016-01-24
相关资源
最近更新 更多