【问题标题】:Delphi Firemonkey - can't add sub-subclass of TTabItem to TTabControlDelphi Firemonkey - 无法将 TTabItem 的子子类添加到 TTabControl
【发布时间】:2019-02-05 15:32:06
【问题描述】:

我可以将 TTabItem 添加到 TTabControl,我可以将 TTabItem 的子类添加到 TabControl,但我不能将 TTabItem 的子类添加到 TabControl。

示例 Firemonkey 应用程序 - 带有 TTabControl 的表单:

type
   TTabItem_subclass = class (TTabItem);
   TTabItem_sub_subclass = class (TTabItem_subclass);

procedure TForm1.FormCreate(Sender: TObject);
 procedure add_tab (t: TTabItem);
   begin
     t.Text := t.ClassName;
     t.Parent := TabControl1
  end;
begin
  add_tab (TTabItem.create (TabControl1));   // <-- works
  add_tab (TTabItem_subclass.create (TabControl1));  // <-- works
  add_tab (TTabItem_sub_subclass.create (TabControl1));  // <-- fails
end;

当应用程序运行时,TTabItem_sub_subclass 不显示:

我在 XE5 和东京都试过这个,结果相同。我错过了什么?

【问题讨论】:

  • 如果你添加第二个TTabItem_subclass 而不是TTabItem_sub_subclassthat 会失败吗?
  • 第二个 TTabItem_subclass 工作正常。我只看到子类的问题。
  • 我可以在 D10.2.3 中重现您的问题,但无法立即看出为什么从 TTabItem_subclass 下降会导致此问题,但我不是 FMX 专家。更新:我想我已经发现为什么子子类不可见,TTabControl 代码中的逻辑阻止它被绘制,但我还没有弄清楚如何修复这个。

标签: delphi


【解决方案1】:

简短回答:我认为您没有遗漏任何内容。如果事实上,您的代码确实成功地将子子类项添加到 TabControl,它只是不会显示。我认为这个问题是由 FMX 代码派生用于绘制作为 TTabItem 子类的类的样式的方式中的缺陷引起的。我对 FMX 的了解不够,无法确定问题的确切原因,但我已经确定了似乎是一种功能性解决方法。

请参阅下面的示例项目代码,它成功显示了两者 TabItem subClass 和 TabItem sub_subClass 选项卡。

代码如此结构化的原因是为了方便设置更改内存断点 TabItem 的 FResourceLink 字段(代码中的变量Item),而我是 试图追踪绘画过程是如何发生的。

通过观察 TabItem.Paint 方法,很明显选项卡只会绘制 如果它的 FResourceLink 不为零。您的原始代码(和我的)的问题 是当Paint 在 TabItem_subClass 上被调用时,它的 FResourceLink 被分配 一个值,而对于 TabItem_sub_subClass 它有 not。显然是 FResourceLink 是它获取用于绘制 TabItem 的样式名称的地方,如果 找不到 TabItem 没有被绘制。

恐怕我不是 FMX 方面的专家,我觉得它的代码有点像迷宫 在最好的时候,它对样式的实现更是如此。但它 让我震惊的是,如果我能确保返回一个有效的样式名称 TabItem GetParentClassStyleLookupName 方法就足够了。这就是原因 对于 TCustomItem_sub_subclass.GetParentClassStyleLookupName 覆盖。我想 FMX 专家可能会认为破解核桃有点像大锤,但是 你去吧。

代码

  type
    TForm1 = class(TForm)
      TabControl1: TTabControl;
      StyleObject1: TStyleObject;  // ignore this
      procedure FormCreate(Sender: TObject);
    private
    public
       Item :  TTabItem;
    end;

  [...]

  implementation

  [...]

  type
     TCustomItem_subclass = class (TTabItem)
     public
       constructor Create(AOwner : TComponent); override;
     end;

     TCustomItem_sub_subclass = class (TCustomItem_subclass)
       public
       constructor Create(AOwner : TComponent); override;
       function GetParentClassStyleLookupName: string; override;
     end;

  procedure TForm1.FormCreate(Sender: TObject);

   procedure add_tab (t: TTabItem);
     begin
       t.Text := t.ClassName;
       t.Parent := TabControl1
    end;

   begin

  {$define UseSubSub}
  {$ifdef UseSubSub}
     Item := TCustomItem_sub_subclass.Create(TabControl1);
  {$else}
     Item := TCustomItem_subclass.Create(TabControl1);
  {$endif}

     Item.Text := Item.ClassName;
     Item.Parent := TabControl1;

     Caption := TabControl1.ActiveTab.Text;

     Item := TCustomItem_subclass.Create(TabControl1);

     Item.Text := Item.ClassName;
     Item.Parent := TabControl1;

  end;

  constructor TCustomItem_subclass.Create(AOwner: TComponent);
  begin
    inherited;
  end;

  constructor TCustomItem_sub_subclass.Create(AOwner: TComponent);
  begin
    inherited;
  end;

  function TCustomItem_sub_subclass.GetParentClassStyleLookupName: string;
  begin
    Result := 'tabitemstyle';
  end;

顺便说一句,在执行此操作时,我注意到函数中似乎有一个潜伏的错误 TStyledControl.GenerateStyleName(const AClassName: string): string 在 FMX.Controls.Pas' 如果 AClassName 参数,去掉前导 TCustom,以双 TT 开头, 与TCustomTabItem 一样,代码错误地删除了TabItem 的T。我没有 时间或精力来进一步探索,但这就是我的 TabItem 子类省略的原因 Tab 来自他们的名字。

【讨论】:

  • 谢谢 - 我印象深刻!这是一个相当简单的解决方法,我永远不会想到。我已将此作为错误报告提交给 Embarcadero (quality.embarcadero.com/browse/RSP-23539),希望他们能在未来的版本中修复它。
  • 我已经验证这解决了 Tokyo 10.2.2 中的问题 - 但是它没有在 XE5 中解决它,因为 GetParentClassStyleLookupName 不在该版本的基类中。尽管如此,非常感谢这种解决方法!
猜你喜欢
  • 2014-09-15
  • 2015-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-23
  • 1970-01-01
  • 2016-02-06
  • 2019-03-12
相关资源
最近更新 更多