【问题标题】:Helper for TWinControlTWinControl 的助手
【发布时间】:2017-05-09 09:14:44
【问题描述】:

我喜欢“覆盖” TWinControl.CNKeyDown,将 Enter 键用作 Tab,而不修改原始 VCL 源。 我也这样做,使用 SetFocus,witch 效果很好。

帮助器类型声明为:

type
   TWinControlEx = class helper for TWinControl
   private
       procedure SetFocus; reintroduce;
       procedure CNKeyDown(var Message: TWMKeyDown); reintroduce;
   end;

被覆盖的方法如下:

if (Message.CharCode = 13) and (Pos('Cr_To_Tab',ImeName)>0) and (Pos('No_Cr_To_Tab',ImeName)=0) then
      begin
        if not ((GetKeyState(VK_CONTROL) and not $7FFF) <> 0) then
        begin
          Message.CharCode := 9;
        end;
      end;
   Message.CharCode := 9;
   inherited;

但似乎我的代码运行时,它仍然使用原始版本。另一个令人沮丧的事情是我无法调试问题,因为当我在 CNKeyDown 方法中放置一个断点时,它永远不会停止,并且 IDE (RAD Studio 10.2) 在其中放置了一个叉号。

谁能给我一些指导?

【问题讨论】:

  • 您的类需要从 TWinControl 继承并用您想要的行为覆盖它。关于调试断点,您需要为您的项目启用调试 dcu - 请参阅调试部分的docwiki.embarcadero.com/RADStudio/Berlin/en/Compiling
  • 它只有在使用助手时才有效,如果我声明像 TWinControlEx = class(TWinControl) 这样的类,那么什么都不会发生,并且调用了更新的函数。我也启用了调试 dcus,但仍然无法调试
  • 这都是错误的。通过 WindowProc 属性进行子类化。

标签: delphi


【解决方案1】:

您不能像尝试那样使用类助手重新引入现有方法的新版本。

类助手不是解决方案。您需要改为:

  1. 从所需的原始类派生一个新类,如TEdit,override 其适用的virtual/dynamic 方法,如WndProc()、KeyDown() 等,然后创建运行时自定义类的实例而不是原始类。您可以在设计时在 IDE 中安装自定义类,也可以将您的类实现为 Interposer,以便它仅在运行时覆盖原始类。有关详细信息,请参阅Two Approaches to Sub-classing Components Compared。

  2. 根本不派生新类。只需在运行时将原始类的所需对象实例的WindowProc 属性子类化即可。然后你可以在他们之前处理他们的窗口消息。

【讨论】:

    【解决方案2】:

    通常,您不必修改 VCL 源来扩展 TWinControl 的后代,因为它有一大堆 virtual 方法,可以使用 override 关键字在后代类中扩展。 SetFocus 就是这样一个虚方法。

    我想知道,你是否真的需要重新实现TWinControl.CNKeyDown。你不能直接覆盖TWinControl.KeyDown吗?

    恰好TWinControl.KeyDown也是一个虚方法。
    实际上,TWinControl.KeyDown 被声明为 dynamic 但这只是一件小事,因为 dynamic 方法是 semantically equivalent 并且可以被覆盖为“普通”virtual 声明的方法。

    关键字reintroduce仅在通过声明具有相同名称的方法而将虚拟方法隐藏在后代类中时使用。然后编译器会发出警告,可以通过使用reintroduce 关键字来避免。

    您的课程应该如下所示:

    界面

    TWinControlEx = class(TWinControl)
    protected
      procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    public
      procedure SetFocus; override;
    end;
    

    实现

    procedure TWinControlEx.KeyDown(var Key: Word; Shift: TShiftState);
    begin
      if (Key = VK_RETURN) and not (ssCtrl in Shift) then
        Key := VK_TAB;
      inherited;
    end;
    
    procedure TWinControlEx.SetFocus;
    begin
      // do something and/or call
      inherited;
    end;
    

    【讨论】:

    • 感谢您的评论,但不幸的是,当我将 twincownrolex 声明为助手时,该方法仅有效(setfocus,keydown 仍然无效)。如果它只是从 twincontrol 派生,则不会发生任何事情。
    • @NemoPeti 你确定,你在派生类中使用了override 关键字吗?如果您(错误地)在您的问题中使用 reintroduced,它将无法正常工作。
    • @NemoPeti 此外,您是否阅读了我的答案链接到的文档章节?我强烈建议这样做,以了解如何使用虚拟方法(在 Delphi 中)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多