如何使用 TStyleManager::UnRegisterStyle() 注销样式
How to unregister style using TStyleManager::UnRegisterStyle()
我想使用此代码注销特定样式:
void __fastcall TfrmMain::btnUnregStyleClick(TObject *Sender)
{
TCustomStyleServices *MyStyle;
// Get wanted style
MyStyle = TStyleManager::Style["Emerald"]; // this will return a TCustomStyleServices obj
if (MyStyle != NULL)
{
// Remove it
TStyleManager::UnRegisterStyle(MyStyle); // This will set default Windows style (no style)
}
}
有效。样式似乎未注册,GUI 自动切换到默认 Windows 样式。
但是当程序关闭时,我得到这个错误:
Project Project.exe raised exception class $C0000005 with message
'access violation at 0x5005fd50: read of address 0xffffffd0'.
这是调用堆栈:
:5005fd50 rtl250.@System@TObject@InheritsFrom$qqrp17System@TMetaClass + 0x8
:50d12a8d vcl250.@Vcl@Styles@TStyleEngine@Notification$qqr54Vcl@Themes@TCustomStyleEngine@TStyleEngineNotificationpv + 0x1d
:00e5a612 vclwinx250.@Vcl@Winxctrls@TSearchBox@$bcdtr$qqrv + 0x1e
:0041fa0f __cleanup + 0x1F
:0041fb92 ; __wstartup
[更新:如果我从我的表单中删除 TeeChart,则此崩溃已修复。但是UnRegisterStyle()
还是不行]
如果在 UnRegisterStyle()
之后我调用:
TStyleManager::LoadFromFile(usStylePath);
TStyleManager::SetStyle("Emerald");
它会告诉我"emerald style is already registered"。
所以,显然 UnRegisterStyle()
失败了。
通过TStyleManager::StyleNames()
获取"styles"列表显示UnRegisterStyle()
后列表不变。
Embarcadero 对此功能没有帮助。除了 UnRegisterStyle()
?
之外,我还应该调用其他东西吗?
答案已更新!再次.
好吧,我对 VCL
的样式不是很有经验,但我会尽力解释如何避免您的问题。在您进一步阅读之前,我应该告诉您:无法注销任何样式。
风格与否
首先,你必须知道VCL
的样式使用内部FRegisteredStyles
字典来存储所有注册的样式。样式在 TStyleManager.LoadFromFile(YourStyleName)
被调用后注册。不幸的是,样式永远不会从字典中删除。
不要使用 UnregisterStyle
过程。它根本不会取消注册样式。只需从 available 样式列表中删除指定样式。因此,在您调用 UnregisterStyle(YourStyle)
之后,您只是从内部列表中擦除 YourStyle
,而不是从前面提到的字典中擦除,并且无法设置此样式。
成功调用 UnregisterStyle()
后,您可能会调用 LoadFromFile()
方法并想知道为什么应用程序说:
Style 'Emerald' already registered.
发生这种情况是因为 LoadFromFile()
方法加载指定样式并检查其是否存在于已注册样式的内部字典中。此检查总是 returns true
as UnregisterStyle()
过程不会从字典中删除指定的样式。
同样的事情也与 属性 StyleNames
有关。此属性 returns 样式名称使用内部FRegisteredStyles
字典获取具有指定索引的样式名称。
如果你想知道我的看法,这种风格行为很奇怪。方法 UnregisterStyle()
还应该从字典中删除指定的样式。也许我错了,但这是一个真正的错误。另一方面,没有错误 - 您正在尝试使用未记录的方法。真相就在附近。
样式化结果
作为结论,我建议您使用直接访问样式来决定您是否可以使用所选样式。请参阅下面的代码(假设您已打开应用程序样式):
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(TStyleManager.Style['Emerald']) and TStyleManager.Style['Emerald'].Available then
// Do your code
else
ShowMessage('Specified style is not available!');
end;
如果你有 VCL.Themes
的来源,你可以很容易地检查我在这里写的内容的合理性。
高科技
我为 TStyleManager
创建了 class-helper
,允许检查样式文件是否已注册并在需要时取消注册。为了用样式文件中的真实值填充 TStyleInfo
记录,我们需要将指定样式保存到 TMemoryStream
,然后将流传递给 IsValidStyle
函数。我们也可以使用 physical path 来设置样式 (f.e. C:\Embarcadero\Delphi\Styles\Emerald.vsf
) 而不是 variant with stream,但后者看起来更优雅。
不幸的是,根据 Remy 的评论(我不知道如何对其进行 link),C++ Builder
(被OP) 不支持 class-helper
功能。唯一的解决方案是创建简单的单元,其中包含 class-helper
所需的所有代码。要使用它就足以将这样的单元添加到 uses
子句并调用适当的 public procedures\functions。
本单元结构如下:
unit StyleManager_CH;
interface
uses
VCL.Themes;
function IsStyleRegistered_CH(var AStyle: TCustomStyleServices): Boolean;
procedure UnregisterStyleEx_CH(var AStyle: TCustomStyleServices);
implementation
uses
System.SysUtils, System.Classes, System.Generics.Collections;
type
TStyleManagerHelper = class helper for TStyleManager
public
class function IsStyleRegistered(var AStyle: TCustomStyleServices): Boolean;
class procedure UnregisterStyleEx(var AStyle: TCustomStyleServices);
end;
class function TStyleManagerHelper.IsStyleRegistered(var
AStyle: TCustomStyleServices): Boolean;
begin
Result := Assigned(AStyle) and
TStyleManager.FRegisteredStyles.ContainsKey(AStyle.Name);
end;
class procedure TStyleManagerHelper.UnregisterStyleEx(var
AStyle: TCustomStyleServices);
var
MS: TMemoryStream;
StyleInfo: TStyleInfo;
SourceInfo: VCL.Themes.TStyleManager.TSourceInfo;
begin
if Assigned(AStyle) then
begin
MS := TMemoryStream.Create;
try
AStyle.SaveToStream(MS);
MS.Position := 0;
if AStyle.IsValidStyle(MS, StyleInfo) then
begin
if TStyleManager.FRegisteredStyles.ContainsKey(StyleInfo.Name) then
begin
SourceInfo := TStyleManager.FRegisteredStyles.Items[StyleInfo.Name];
if Assigned(SourceInfo.Data) then
FreeAndNil(SourceInfo.Data);
TStyleManager.FStyles.Remove(AStyle);
TStyleManager.FRegisteredStyles.Remove(StyleInfo.Name);
FreeAndNil(AStyle);
end;
end;
finally
MS.Free;
end;
end;
end;
function IsStyleRegistered_CH(var AStyle: TCustomStyleServices): Boolean;
begin
Result := TStyleManager.IsStyleRegistered(AStyle);
end;
procedure UnregisterStyleEx_CH(var AStyle: TCustomStyleServices);
begin
TStyleManager.UnregisterStyleEx(AStyle);
end;
end.
附录 _CH
代表 class-helper
缩写。还修复了一些内存泄漏问题。
我不确定是否有其他方法可以重新加载样式文件"on-the-fly"。
我想使用此代码注销特定样式:
void __fastcall TfrmMain::btnUnregStyleClick(TObject *Sender)
{
TCustomStyleServices *MyStyle;
// Get wanted style
MyStyle = TStyleManager::Style["Emerald"]; // this will return a TCustomStyleServices obj
if (MyStyle != NULL)
{
// Remove it
TStyleManager::UnRegisterStyle(MyStyle); // This will set default Windows style (no style)
}
}
有效。样式似乎未注册,GUI 自动切换到默认 Windows 样式。
但是当程序关闭时,我得到这个错误:
Project Project.exe raised exception class $C0000005 with message 'access violation at 0x5005fd50: read of address 0xffffffd0'.
这是调用堆栈:
:5005fd50 rtl250.@System@TObject@InheritsFrom$qqrp17System@TMetaClass + 0x8 :50d12a8d vcl250.@Vcl@Styles@TStyleEngine@Notification$qqr54Vcl@Themes@TCustomStyleEngine@TStyleEngineNotificationpv + 0x1d :00e5a612 vclwinx250.@Vcl@Winxctrls@TSearchBox@$bcdtr$qqrv + 0x1e :0041fa0f __cleanup + 0x1F :0041fb92 ; __wstartup
[更新:如果我从我的表单中删除 TeeChart,则此崩溃已修复。但是UnRegisterStyle()
还是不行]
如果在 UnRegisterStyle()
之后我调用:
TStyleManager::LoadFromFile(usStylePath);
TStyleManager::SetStyle("Emerald");
它会告诉我"emerald style is already registered"。
所以,显然 UnRegisterStyle()
失败了。
通过TStyleManager::StyleNames()
获取"styles"列表显示UnRegisterStyle()
后列表不变。
Embarcadero 对此功能没有帮助。除了 UnRegisterStyle()
?
答案已更新!再次.
好吧,我对 VCL
的样式不是很有经验,但我会尽力解释如何避免您的问题。在您进一步阅读之前,我应该告诉您:无法注销任何样式。
风格与否
首先,你必须知道VCL
的样式使用内部FRegisteredStyles
字典来存储所有注册的样式。样式在 TStyleManager.LoadFromFile(YourStyleName)
被调用后注册。不幸的是,样式永远不会从字典中删除。
不要使用 UnregisterStyle
过程。它根本不会取消注册样式。只需从 available 样式列表中删除指定样式。因此,在您调用 UnregisterStyle(YourStyle)
之后,您只是从内部列表中擦除 YourStyle
,而不是从前面提到的字典中擦除,并且无法设置此样式。
成功调用 UnregisterStyle()
后,您可能会调用 LoadFromFile()
方法并想知道为什么应用程序说:
Style 'Emerald' already registered.
发生这种情况是因为 LoadFromFile()
方法加载指定样式并检查其是否存在于已注册样式的内部字典中。此检查总是 returns true
as UnregisterStyle()
过程不会从字典中删除指定的样式。
同样的事情也与 属性 StyleNames
有关。此属性 returns 样式名称使用内部FRegisteredStyles
字典获取具有指定索引的样式名称。
如果你想知道我的看法,这种风格行为很奇怪。方法 UnregisterStyle()
还应该从字典中删除指定的样式。也许我错了,但这是一个真正的错误。另一方面,没有错误 - 您正在尝试使用未记录的方法。真相就在附近。
样式化结果
作为结论,我建议您使用直接访问样式来决定您是否可以使用所选样式。请参阅下面的代码(假设您已打开应用程序样式):
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(TStyleManager.Style['Emerald']) and TStyleManager.Style['Emerald'].Available then
// Do your code
else
ShowMessage('Specified style is not available!');
end;
如果你有 VCL.Themes
的来源,你可以很容易地检查我在这里写的内容的合理性。
高科技
我为 TStyleManager
创建了 class-helper
,允许检查样式文件是否已注册并在需要时取消注册。为了用样式文件中的真实值填充 TStyleInfo
记录,我们需要将指定样式保存到 TMemoryStream
,然后将流传递给 IsValidStyle
函数。我们也可以使用 physical path 来设置样式 (f.e. C:\Embarcadero\Delphi\Styles\Emerald.vsf
) 而不是 variant with stream,但后者看起来更优雅。
不幸的是,根据 Remy 的评论(我不知道如何对其进行 link),C++ Builder
(被OP) 不支持 class-helper
功能。唯一的解决方案是创建简单的单元,其中包含 class-helper
所需的所有代码。要使用它就足以将这样的单元添加到 uses
子句并调用适当的 public procedures\functions。
本单元结构如下:
unit StyleManager_CH;
interface
uses
VCL.Themes;
function IsStyleRegistered_CH(var AStyle: TCustomStyleServices): Boolean;
procedure UnregisterStyleEx_CH(var AStyle: TCustomStyleServices);
implementation
uses
System.SysUtils, System.Classes, System.Generics.Collections;
type
TStyleManagerHelper = class helper for TStyleManager
public
class function IsStyleRegistered(var AStyle: TCustomStyleServices): Boolean;
class procedure UnregisterStyleEx(var AStyle: TCustomStyleServices);
end;
class function TStyleManagerHelper.IsStyleRegistered(var
AStyle: TCustomStyleServices): Boolean;
begin
Result := Assigned(AStyle) and
TStyleManager.FRegisteredStyles.ContainsKey(AStyle.Name);
end;
class procedure TStyleManagerHelper.UnregisterStyleEx(var
AStyle: TCustomStyleServices);
var
MS: TMemoryStream;
StyleInfo: TStyleInfo;
SourceInfo: VCL.Themes.TStyleManager.TSourceInfo;
begin
if Assigned(AStyle) then
begin
MS := TMemoryStream.Create;
try
AStyle.SaveToStream(MS);
MS.Position := 0;
if AStyle.IsValidStyle(MS, StyleInfo) then
begin
if TStyleManager.FRegisteredStyles.ContainsKey(StyleInfo.Name) then
begin
SourceInfo := TStyleManager.FRegisteredStyles.Items[StyleInfo.Name];
if Assigned(SourceInfo.Data) then
FreeAndNil(SourceInfo.Data);
TStyleManager.FStyles.Remove(AStyle);
TStyleManager.FRegisteredStyles.Remove(StyleInfo.Name);
FreeAndNil(AStyle);
end;
end;
finally
MS.Free;
end;
end;
end;
function IsStyleRegistered_CH(var AStyle: TCustomStyleServices): Boolean;
begin
Result := TStyleManager.IsStyleRegistered(AStyle);
end;
procedure UnregisterStyleEx_CH(var AStyle: TCustomStyleServices);
begin
TStyleManager.UnregisterStyleEx(AStyle);
end;
end.
附录 _CH
代表 class-helper
缩写。还修复了一些内存泄漏问题。
我不确定是否有其他方法可以重新加载样式文件"on-the-fly"。