【问题标题】:VCL Styles breaks randomlyVCL Styles 随机中断
【发布时间】:2015-02-11 20:09:55
【问题描述】:

我有一个源自 TMemo 的控件。在我第一次使用 Delphi XE7 VCL Styles 之前,它一直很好用。在 Delphi XE7 下,样式不会应用于控件的滚动条。如果使用深色主题/样式,它看起来很糟糕,而滚动条是银色的。

尝试创建一个可以重现错误的最小项目,我发现了一些非常有趣的事情:添加/删除随机代码行(或 DFM 控件)将使错误出现/消失。

问题:究竟是什么导致了这种奇怪的行为以及如何解决它?

源码在这里:

http://s000.tinyupload.com/index.php?file_id=24129853712119260018

【问题讨论】:

  • 据我了解WMSetText 是一个消息处理程序。代码Message.Text := PChar(s); 传递带有指向堆栈中变量的指针的消息,该变量在此过程之外不可用...
  • @Altar,尝试为您的自定义备忘录控件注册TMemoStyleHook 样式钩子,就像TStyleManager.Engine.RegisterStyleHook(TMyMemo, TMemoStyleHook);
  • 您的问题是您的消息处理程序是“公开的”。使其“受保护”。其他消息处理程序也是如此。
  • 另一个想法(但我们需要 David 的 repro 项目) - 由于该项目仅包含 Embarcadero 代码(没有自定义库),我们为什么不称之为 Delphi 错误? (并报告)。

标签: delphi delphi-xe7 vcl-styles


【解决方案1】:

为自定义类注册StyleHook可以解决问题:

  TMyMemo = class(TMemo)
  strict private
    class constructor Create;
    class destructor Destroy;
  end;

class constructor TMyMemo.Create;
begin
  TCustomStyleEngine.RegisterStyleHook(TMyMemo, TMemoStyleHook);
end;

class destructor TMyMemo.Destroy;
begin
  TCustomStyleEngine.UnRegisterStyleHook(TMyMemo, TMemoStyleHook);
end;

TStyleEngine.HandleMessage 函数中存在错误,特别是试图找到合适的StyleHook 类来处理消息

if RegisteredStyleHooks.ContainsKey(Control.ClassType) then
  // The easy way: The class is registered
  LStyleHook := CreateStyleHook(RegisteredStyleHooks[Control.ClassType])
else
begin
  // The hard way: An ancestor is registered
  for LItem in RegisteredStyleHooks do
    if Control.InheritsFrom(LItem.Key) then
    begin
      LStyleHook := CreateStyleHook(Litem.Value);
      Break;
    end;

如果StyleHook 注册为精确类,则没有问题,并且将返回适当的StyleHook 类。然而,“困难的方式”部分是有缺陷的。它将尝试查找已注册StyleHook 的类祖先。但它会返回它遇到的第一个祖先。如果它首先找到TEditStyleHook(即注册为TCustomEdit 类),它将使用那个而不是TMemoStyleHook。由于TEditStyleHook 不知道如何处理滚动条问题出现。

错误行为的随机性归因于 RegisteredStyleHooks 的存储方式。它们存储在字典中,其中键为TClass。并且顺序由 TClass 哈希确定,它基本上是指向类信息的指针,并且可以随着您更改代码而更改。

问题被报告为RSP-10066,并且有一个重现它的附加项目。

在以下代码的帮助下,可以很容易地看到注册类的顺序在您添加/删除代码和/或其他控件时如何变化。

type
  TStyleHelper = class(TCustomStyleEngine)
  public
    class function GetClasses: TArray<TClass>;
  end;

class function TStyleHelper.GetClasses: TArray<TClass>;
begin
  Result := Self.RegisteredStyleHooks.Keys.ToArray;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  LItem: TClass;
  Classes: TArray<TClass>;
begin
  Classes := TStyleHelper.GetClasses;
  for LItem in Classes do
    MyMemo1.Lines.Add(LItem.ClassName);
end;

【讨论】:

  • 我认为这根本无法解释。
  • 很公平。在任何情况下 +1 以查找可能是也可能不是问题的唯一原因的错误。
  • @SertacAkyuz 我发现了为什么添加/删除代码会改变行为。注册的 StyleHooks 存储在字典中,其中 key 是 TClass。顺序由 TClass 哈希决定,它基本上是指向类信息的指针,并且可以随着您更改代码而更改。
  • 这应该在答案中。那么问题是for LItem in RegisteredStyleHooks 以不明确的顺序迭代。干得好。
  • @SertacAkyuz,但 Olivier Sannier 的分析是正确的:“嗯,当 TStyleEngine.HandleMessage 中的代码试图找到 TMyForm 实例的挂钩时,它会找到 TCustomForm 的挂钩在 TBaseForm 之前,其后果可想而知。对于“简单的方法”,使用字典是一个好主意,但显然,对于“困难的方法”,应该有一个按继承排序的列表。”
猜你喜欢
  • 2012-04-16
  • 1970-01-01
  • 2015-10-20
  • 1970-01-01
  • 2012-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多