Adding VCL Styles at runtime

VCL Styles

An example before & after

Screen Shot 2014-10-22 at 06.09.20VCL Styles at run time

VCL Styles provide a great way to update the look and feel of your VCL applications.  VCL Styles can be easily added into an application before you compile through  project options > appearance options.

Adding VCL Styles to Project : Options : Appearance
Adding VCL Styles via Project : Options : Appearance

While you can add VCL Styles into the application binary this way, they will increase the size of the application. If you want to avoid this, (or maybe run a mix of compiled in and dynamically loaded) you can distribute styles along side your application.

By default, VCL Styles are located at the following file path when installing your IDE.

C:\Users\Public\Documents\Embarcadero\Studio\15.0\Styles

A full overview of them is available on docwiki  http://docwiki.embarcadero.com/RADStudio/XE7/en/VCL_Styles_Overview

TStyleManager

Styles are managed via TStyleManager, a class located in unit VCL.Themes.

TStyleManager is a sealed class with a number of class methods that allow you to interact with the global application settings for the styles.

Some key properties on TStyleManager

  • TStyleManager.StyleNames – an array of the styles available in the application.
  • TStyleManager.SetStyle(StyleName : string) – the call to set the application style to a new style

Using these basic methods, its possible to implement code that loops the styles in the application and makes them available to a menu that can then be used at run time to choose which style is selected.

  • TStyleManager.IsValidStyle(FileName: string) – checks a file is a valid style
  • TStyleManager.LoadFromFile(FileName : string) – loads a style up from file.

With the additional file based calls its possible to check for style files and load those into your application as well.  – So lets do that!

VCL Styles code example

The MastApp demo that is installed by default with RAD Studio has an example of using TStyleManager. You can find the demo at:

C:\Users\Public\Documents\Embarcadero\Studio\15.0\Samples\Object Pascal\Database\IBX\IBMastApp

This demo uses 2 code parts and 1 design time selection element to provide styles in the demo.

Part 1 – Compiling VCL Styles into your application.

Open the project and then choose Project > Options and go to appearance. As above, select a couple of styles and run the application. You will then see the Styles menu is updated to show these styles.

The initial code to build the menu loops the StyleNames, creates a menu item for each style, adds then links to an OnClick event to change the style.

Part 2 – Building the style list menu

procedure TMainForm.FormCreate(Sender: TObject);
var
 Style: String;
 Item: TMenuItem;
begin
 //Add child menu items based on available styles.
 for Style in TStyleManager.StyleNames do
 begin
   Item := TMenuItem.Create(StyleMenu);
   Item.Caption := Style;
   Item.OnClick := StyleClick;
   if TStyleManager.ActiveStyle.Name = Style then
     Item.Checked := true;
   StyleMenu.Add(Item);
 end;
end;

Step 3 – OnClick event for the Style selected.

procedure TMainForm.StyleClick(Sender: TObject);
var
 StyleName: String;
 i: Integer;
begin
 //get style name
 StyleName :=   StringReplace(TMenuItem(Sender).Caption, '&', '',
 [rfReplaceAll, rfIgnoreCase]);
 //set active style
 TStyleManager.SetStyle(StyleName);
 //check the currently selected menu item
 (Sender as TMenuItem).Checked := true;
 //uncheck all other style menu items
 for I := 0 to StyleMenu.Count -1 do begin
   if not StyleMenu.Items[i].Equals(Sender) then
     StyleMenu.Items[i].Checked := false;
 end;

end;

So: We have styles working, but you can also load them from file with very little change to the application.

Loading Styles from file and displaying them in the list of styles

VCL Styles can be loaded at run time easily using TStyleManager.LoadFromFile

Using the existing demo, its possible to have it check for a styles sub folder and read any file in there to see if its a valid style.  To do this, we just need to use FindFirst, iterate the returned files and then if they are valid (something we can use TStyleManager.IsValidStyle to determine) load the files.

So the updated code looks something like this block below where we first build the path to check for VCL Style files; then check the folder exists, and if it does check each file to see if its a valid VCL Style (before loading it).  Secondly, using a TStringList the styles loaded are sorted and then the sorted list is used now to build a menu for selecting the styles.

procedure TMainForm.FormCreate(Sender: TObject);
var
 Style: String;
 Item: TMenuItem;
 i: integer;
 searchResults : TSearchRec;
 SearchDir: string;
 SLStyles: TStringList;
begin
 SearchDir := ExtractFilePath(ParamStr(0))  
                 +'styles'+ PathDelim;
 if DirectoryExists(SearchDir) then begin
   if FindFirst(SearchDir+'*.*',  
                faAnyFile - faDirectory,
                searchResults) = 0 then
   repeat
     try
       if TStyleManager.IsValidStyle(
            SearchDir+searchResults.Name) then
          TStyleManager.LoadFromFile(
             SearchDir+searchResults.Name);
     except
       // Who cares.. try the next one.
     end;
   until FindNext(searchResults) <> 0;
 end;
 // Sort the styles using a StringList 
 SLStyles := TStringList.Create;
 try
   SLStyles.Duplicates := TDuplicates.dupIgnore;
   for Style in TStyleManager.StyleNames do
     SLStyles.Add(Style);
   SLStyles.Sort;

   PMStyles.Items.Clear;
   // build menu from sorted list of styles
   for Style in SLStyles do begin
     Item := TMenuItem.Create(StyleMenu);   
     Item.Caption := Style; 
     Item.OnClick := StyleClick; 
     if TStyleManager.ActiveStyle.Name=Style then 
       Item.Checked := true; 
     StyleMenu.Add(Item);
   end;
 finally
   SLStyles.Free;
 end;
end;

Thats it! You can now create a sub folder for VCL styles that you ship along side your application or deploy at a later point.

For C++ use the same classes and units, just with the C++ Loops and constructs. more on VCL Styles with C++ can be found on docwiki

 

7 thoughts on “Adding VCL Styles at runtime”

  1. This article is almost 2 years old now, but I found it very instructive ! As the rest of the blog I just stumbled upon today 🙂

    One question regarding VCL styles : what you describes lets the application user choose between available styles we embedded or joined with the application.
    But on application close and restart, the style switches back to the default one.

    If you were to let the user choose his own style, how would you “save” his choice for him to keep his own style on next application start ? Ini file ? Database “option” table ?

    Thanks in advance

    Mathias from France

    1. Hi Mathias, I would suggest reading a saved option from either an INI files, Database table (anywhere really) and just load and set that style as your application starts. Its just a string at the end of the day.

  2. I got a problem that the styles are not available in my exe compiled on a buildserver with msbuild.
    On the build server Delphi isn’t installed.
    When I build the project on my machine with delphi I can choose styles.
    Any Ideas on how to solve this?

    1. Not tried…. you could look at copying over the stlyes from the redist folder and try putting them in the same location on the build machine. This is a great question to ask support as part of your update subscription. 🙂

  3. Very informative article. Thank you, Stephen.

    How can I propagate the chosen style from the EXE to all subsequently loaded DLLs? In my case, the EXE is simply “container” with menu and DLLs are loaded per menu selection. To have a consistent look and feel across all the modules, the style chosen in EXE needs to be applied to all other DLLs as well. Any ideas?

    1. Interesting approach… You can ship the styles as external files. I would suggest having a folder that the styles live in, and then pass a reference to the DLL for the style to apply. You can then load at runtime from file and apply to the forms being shown. This also means you don’t need to keep all DLL’s up-to-date with the latest styles. (The other option is to rename a style to current and have that as a hard coded option)

Leave a Reply

Your email address will not be published. Required fields are marked *