I want to unregister a specific style using this code:
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)
}
}
It works. The style seems to be unregistered, and the GUI switches automatically to the default Windows style.
But when program shuts down, I get this error:
Project Project.exe raised exception class $C0000005 with message 'access violation at 0x5005fd50: read of address 0xffffffd0'.
Here is the Call stack:
: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
[Update: this crash is fixed if I remove TeeChart from my form. But UnRegisterStyle()
still won't work]
If after UnRegisterStyle()
I call:
TStyleManager::LoadFromFile(usStylePath);
TStyleManager::SetStyle("Emerald");
it will tell me that the "emerald style is already registered".
So, obviously UnRegisterStyle()
fails.
Getting the "styles" list via TStyleManager::StyleNames()
shows that the list remains unchanged after UnRegisterStyle()
.
Embarcadero has no help on this function. Should I call something else, additionally to UnRegisterStyle()
?
Answer has been updated! Again.
Well, I am not very experienced user of VCL
's styles but I will try to explain what to do to avoid your problem. Before you will read further I should tell you: there is no way to unregister any style.
First, you must know that VCL
's styles uses internal FRegisteredStyles
dictionary to store all registered styles. Style became registered after TStyleManager.LoadFromFile(YourStyleName)
has been called. Unfortunately, style never gets removed from dictionary.
Do not use UnregisterStyle
procedure. It doesn't unregister style at all. Simply removes specified style from the list of available styles. Thus, after you call UnregisterStyle(YourStyle)
you just wipe out YourStyle
from internal list, not from dictionary mentioned earlier and cannot set this style.
After successful call to UnregisterStyle()
you may call LoadFromFile()
method and wonder why applicaition said:
Style 'Emerald' already registered.
This happens because LoadFromFile()
method loads specified style and checks its existing in internal dictionary of registered styles. This checking always returns true
as UnregisterStyle()
procedure doesn't delete specified style from dictionary.
The same thing related also with property StyleNames
. This property returns name of style uses internal FRegisteredStyles
dictionary to obtain name of style with specified index.
If you want to know my opinion, such behaviour of style is weird. Method UnregisterStyle()
should also delete specified style from dictionary. Maybe I am wrong but this is a real bug. On the other hand, there is no bug - you are trying to use undocumented method. The truth is somewhere near.
As conclusion, I would recommend you to use direct access to styles to decide can you use the selected style or not. See the code below (assuming you have styling of application turned on):
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;
If you have sources of VCL.Themes
you can easily check the plausibility of what I wrote here.
I have created class-helper
for TStyleManager
that allows to check if style-file registered and unregister it if needed. In order to fill TStyleInfo
record with real values from style-file, we need save specified style to TMemoryStream
and then pass stream to IsValidStyle
function. Also we could use physical path to style (f.e. C:\Embarcadero\Delphi\Styles\Emerald.vsf
) instead of variant with stream, but latter looks more elegant.
Unfortunately, according to Remy's comment (I don't know how to make a link to it), C++ Builder
(which is used by OP) doesn't support class-helper
feature. The only solution is to create simple unit which contains all code needed for class-helper
. To work with it is enough to add such unit to uses
clause and call appropriate public procedures\functions.
This unit have the following structure:
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.
Addendum _CH
stands for class-helper
abbreviation. There were also fixed some memory leaks.
I am not sure if there is another way to reload style-file "on-the-fly".