Delphi VCL样式随机中断

Delphi VCL样式随机中断,delphi,delphi-xe7,vcl-styles,Delphi,Delphi Xe7,Vcl Styles,我有一个来自TMemo的控件。在我第一次使用Delphi XE7 VCL样式之前,它工作得很好。在Delphi XE7下,样式不会应用于控件的滚动条。如果使用黑色主题/样式,它看起来很恐怖,而滚动条是银色的 我发现了一件非常有趣的事情:添加/删除随机代码行(或DFM控件),这会使错误出现/消失 问题:到底是什么导致了这种奇怪的行为,以及如何修复它 源代码如下: 为自定义类注册StyleHook解决了以下问题: TMyMemo = class(TMemo) strict private

我有一个来自TMemo的控件。在我第一次使用Delphi XE7 VCL样式之前,它工作得很好。在Delphi XE7下,样式不会应用于控件的滚动条。如果使用黑色主题/样式,它看起来很恐怖,而滚动条是银色的

我发现了一件非常有趣的事情:添加/删除随机代码行(或DFM控件),这会使错误出现/消失

问题:到底是什么导致了这种奇怪的行为,以及如何修复它

源代码如下:

为自定义类注册
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
不知道如何处理滚动条,所以出现了这个问题

buggy行为的随机性是由于
RegisteredStyleHooks
的存储方式造成的。它们存储在字典中,其中键为
TClass
。顺序由
TClass
散列决定,散列基本上是指向类信息的指针,可以随着代码的更改而更改

问题被报告为,并有一个附加的项目,复制它

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

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;
类型
TStyleHelper=类(TCustomStyleEngine)
公众的
类函数GetClasses:TArray;
结束;
类函数TStyleHelper.GetClasses:TArray;
开始
结果:=Self.RegisteredStyleHooks.Keys.ToArray;
结束;
程序TForm1.按钮1单击(发送方:TObject);
变量
LItem:TClass;
类别:柏油虫;
开始
类:=TStyleHelper.GetClasses;
对于类中的LItem,请执行以下操作:
MyMemo1.Lines.Add(LItem.ClassName);
结束;

据我所知,
WMSetText
是一个消息处理程序。代码Message.Text:=PChar通过指向堆栈中的var的指针传递消息,该var在此过程之外不可用…@table,尝试为自定义备忘录控件注册
TMemoStyleHook
样式挂钩,如so
TStyleManager.Engine.registerstyle挂钩(TMyMemo,TMemoStyleHook)您的问题是您的消息处理程序是“公共的”。让它“受保护”。还有一个想法(但我们需要David的Repo项目)——既然项目只包含Embarcadero代码(没有自定义库),为什么我们不把它称为Delphi bug呢?(并报告它)。报告,因为我认为这根本不能解释它。很公平+1在任何情况下,查找可能是或可能不是问题唯一原因的错误。@SertacAkyuz我找到了添加/删除代码改变行为的原因。注册的样式挂钩存储在字典中,其中键是TClass。顺序由TClass散列决定,它基本上是指向类信息的指针,并且可以随着代码的更改而更改。答案应该是这样的。然后,问题是RegisteredStyleHooks中的LItem的
以错误定义的顺序迭代。做得好。@SertacAkyuz,但Olivier Sannier的分析是正确的:“好吧,当TStyleEngine.HandleMessage中的代码试图找到TMyForm实例的钩子时,它会先找到TCustomForm的钩子,再找到TBaseForm的钩子,其后果是可以想象的。使用字典是“简单方法”的一个好主意,但很明显,应该有一个按继承顺序排列的“艰难之路”列表