Delphi VCL样式随机中断
我有一个来自TMemo的控件。在我第一次使用Delphi XE7 VCL样式之前,它工作得很好。在Delphi XE7下,样式不会应用于控件的滚动条。如果使用黑色主题/样式,它看起来很恐怖,而滚动条是银色的 我发现了一件非常有趣的事情:添加/删除随机代码行(或DFM控件),这会使错误出现/消失 问题:到底是什么导致了这种奇怪的行为,以及如何修复它 源代码如下: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
为自定义类注册
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
样式挂钩,如soTStyleManager.Engine.registerstyle挂钩(TMyMemo,TMemoStyleHook)代码>您的问题是您的消息处理程序是“公共的”。让它“受保护”。还有一个想法(但我们需要David的Repo项目)——既然项目只包含Embarcadero代码(没有自定义库),为什么我们不把它称为Delphi bug呢?(并报告它)。报告,因为我认为这根本不能解释它。很公平+1在任何情况下,查找可能是或可能不是问题唯一原因的错误。@SertacAkyuz我找到了添加/删除代码改变行为的原因。注册的样式挂钩存储在字典中,其中键是TClass。顺序由TClass散列决定,它基本上是指向类信息的指针,并且可以随着代码的更改而更改。答案应该是这样的。然后,问题是RegisteredStyleHooks中的LItem的以错误定义的顺序迭代。做得好。@SertacAkyuz,但Olivier Sannier的分析是正确的:“好吧,当TStyleEngine.HandleMessage中的代码试图找到TMyForm实例的钩子时,它会先找到TCustomForm的钩子,再找到TBaseForm的钩子,其后果是可以想象的。使用字典是“简单方法”的一个好主意,但很明显,应该有一个按继承顺序排列的“艰难之路”列表