17

Very much like the "Project|Options|Application|Enable runtime themes" CheckBox, but dynamically at run-time instead.
[Delphi XE targetting Win XP or Win 7]

I tried playing a bit with uxTheme.SetWindowTheme without success so far....

Francesca
  • 21,452
  • 4
  • 49
  • 90

3 Answers3

15

Just for complement the Rob Kennedy answer, you must use the SetThemeAppProperties in this way.

uses
 UxTheme;

procedure DisableThemesApp;
begin
  SetThemeAppProperties(0);
  SendMessage(Application.Handle,WM_THEMECHANGED,0,0);
  SendMessage(Application.MainForm.Handle,CM_RECREATEWND,0,0);
end;

procedure EnableThemesApp;
begin
  SetThemeAppProperties(STAP_ALLOW_NONCLIENT or STAP_ALLOW_CONTROLS or STAP_ALLOW_WEBCONTENT);
  SendMessage(Application.Handle,WM_THEMECHANGED,0,0);
  SendMessage(Application.MainForm.Handle,CM_RECREATEWND,0,0);
end;

and to determine if your controls are themed or not you can use the GetThemeAppProperties function.

var
  Flag : DWORD;
begin
  Flag:=GetThemeAppProperties;
  if (Flag and STAP_ALLOW_CONTROLS)<>0 then //if the controls are themed
  begin

  end;
end;

UPDATE

Due to the issues described for you , i check the code of the UxTheme unit and i see the problem is related to the UseThemes function . so i wrote this small patch (using the functions to patch HookProc, UnHookProc and GetActualAddr developed by Andreas Hausladen), which works ok on my tests. let my know if works for you too.

you must include the PatchUxTheme in your uses list. and call the functions DisableThemesApp and EnableThemesApp.

unit PatchUxTheme;

interface


procedure EnableThemesApp;
procedure DisableThemesApp;


implementation

uses
Controls,
Forms,
Messages,
UxTheme,
Sysutils,
Windows;

type
  TJumpOfs = Integer;
  PPointer = ^Pointer;

  PXRedirCode = ^TXRedirCode;
  TXRedirCode = packed record
    Jump: Byte;
    Offset: TJumpOfs;
  end;

  PAbsoluteIndirectJmp = ^TAbsoluteIndirectJmp;
  TAbsoluteIndirectJmp = packed record
    OpCode: Word;
    Addr: PPointer;
  end;

var
 UseThemesBackup: TXRedirCode;

function GetActualAddr(Proc: Pointer): Pointer;
begin
  if Proc <> nil then
  begin
    if (Win32Platform = VER_PLATFORM_WIN32_NT) and (PAbsoluteIndirectJmp(Proc).OpCode = $25FF) then
      Result := PAbsoluteIndirectJmp(Proc).Addr^
    else
      Result := Proc;
  end
  else
    Result := nil;
end;


procedure HookProc(Proc, Dest: Pointer; var BackupCode: TXRedirCode);
var
  n: DWORD;
  Code: TXRedirCode;
begin
  Proc := GetActualAddr(Proc);
  Assert(Proc <> nil);
  if ReadProcessMemory(GetCurrentProcess, Proc, @BackupCode, SizeOf(BackupCode), n) then
  begin
    Code.Jump := $E9;
    Code.Offset := PAnsiChar(Dest) - PAnsiChar(Proc) - SizeOf(Code);
    WriteProcessMemory(GetCurrentProcess, Proc, @Code, SizeOf(Code), n);
  end;
end;

procedure UnhookProc(Proc: Pointer; var BackupCode: TXRedirCode);
var
  n: Cardinal;
begin
  if (BackupCode.Jump <> 0) and (Proc <> nil) then
  begin
    Proc := GetActualAddr(Proc);
    Assert(Proc <> nil);
    WriteProcessMemory(GetCurrentProcess, Proc, @BackupCode, SizeOf(BackupCode), n);
    BackupCode.Jump := 0;
  end;
end;

function UseThemesH:Boolean;
Var
 Flag : DWORD;
begin
  Flag:=GetThemeAppProperties;
  if ( (@IsAppThemed<>nil) and (@IsThemeActive<>nil) ) then
    Result := IsAppThemed and IsThemeActive and ((Flag and STAP_ALLOW_CONTROLS)<>0)
  else
    Result := False;
end;

procedure HookUseThemes;
begin
  HookProc(@UxTheme.UseThemes, @UseThemesH, UseThemesBackup);
end;

procedure UnHookUseThemes;
begin
  UnhookProc(@UxTheme.UseThemes, UseThemesBackup);
end;


Procedure DisableThemesApp;
begin
  SetThemeAppProperties(0);
  SendMessage(Application.Handle,WM_THEMECHANGED,0,0);
  SendMessage(Application.MainForm.Handle,CM_RECREATEWND,0,0);
end;

Procedure EnableThemesApp;
begin
  SetThemeAppProperties(STAP_ALLOW_NONCLIENT or STAP_ALLOW_CONTROLS or STAP_ALLOW_WEBCONTENT);
  SendMessage(Application.Handle,WM_THEMECHANGED,0,0);
  SendMessage(Application.MainForm.Handle,CM_RECREATEWND,0,0);
end;

initialization
 HookUseThemes;
finalization
 UnHookUseThemes;
end.
Warren P
  • 65,725
  • 40
  • 181
  • 316
RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • @RRUZ. Getting there but not quite yet... CM_RECREATEWND was definitely needed to see anything (although I would avoid it because the nasty side-effects it can bring on Combos, ListViews...). There are still problems when removing theme with **SpeedButtons disappearing, PageControls not not repainting when changing tab, and Grids being a display mess**. One of the reasons might be because **`IsAppThemed and IsThemeActive`** still returns `True` which confuses the VCL when trying to paint... – Francesca Dec 09 '10 at 19:15
  • @François, do you see similar problems if you change the theme setting globally from the control panel? – Rob Kennedy Dec 09 '10 at 19:49
  • @Rob. Very good question. It's not as bad when removing the theme in the control panel (to Windows Classic). Only problem in that case seem to be the grids cells painting. The SpeedButtons and PageControls behave correctly. Now the interesting part is that changing in the Control Panel AND changing in the app with the code above works OK (everyone seems to behave). – Francesca Dec 09 '10 at 20:08
  • @Rob. And even more interesting when I have turned themes off from Control Panel and Application with code, if I re-enable **first** in the app then in Control Panel, themes come back, while if I do Control Panel 1st then with code in the App, themes do not come back. – Francesca Dec 09 '10 at 20:22
  • @RRUZ. Seems like a workable solution. The TToolBars still misbehave a little bit upon removing the Theme but some fiddling can probably fix it. It might not be THE solution though, as it doesn't do anything for when you remove the Theme from the Control Panel... And BTW, Delphi itself does not handle that well either. ;-) Thanks Rodrigo (and Rob)! – Francesca Dec 09 '10 at 21:35
  • @RRUZ I had been using the manifest file for themeing, where the user would enable it and restart the program for it to take effect. But that working a while ago. My question is, does this replace the manifest file method. – Rohit Gupta Jul 01 '15 at 20:36
4

Call SetThemeAppProperties.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • Hmm. Seems it's not working with my D2010 at home. `SetThemeAppProperties(0)` does not seem to have any visible effect. `IsAppThemed and IsThemeActive` still returns `True` with or without a `WM_THEMECHANGED` or calling `ThemeServices.ApplyThemeChange`. I'll try more at work tomorrow with Delphi XE... – Francesca Dec 09 '10 at 08:25
1

For one of my projects I used something like this:

Procedure RemoveTheme(Const Controls : Array Of HWnd; Const Redraw : Boolean = True);
Var
  I : Integer;
Begin
  If IsAppThemed And IsThemeActive Then Try
    I := 0;
    While (I < Length(Controls)) Do Begin
      If (Controls[I] > 0) And IsWindow(Controls[I]) Then SetWindowTheme(Controls[I], '', '');
      If Redraw Then Begin
        InvalidateRect(Controls[I], Nil, True);
        UpdateWindow(Controls[I]);
      End;
      Inc(I);
    End;
  Except
  End;
End;

Use like: RemoveTheme([Edit1.Handle, Edit2.Handle]);

menjaraz
  • 7,551
  • 4
  • 41
  • 81
BoB
  • 71
  • 4
  • Thanks, but it doesn't work in my case. (a) you need to recurse down containers (panels, boxes, tab/page controls...), (b) controls that are not WinControls (graphic controls like SpeedButtons...) are not handled, (c) dialogs that are not defined by the application (windows.MessageBox...) gets themed anyway, (d) controls painted by the VCL like Grids get partially changed (ScrollBars changed by Windows, cells not changed by the VCL). I'd rather set a global flag and tell Windows/the Theme Manager/the VCL that this application is not themed. If at all possible.... – Francesca Dec 09 '10 at 19:34