2012-04-01 8 views
8

So che è possibile utilizzare SetWindowTheme trovato in uxTheme.pas per abilitare/disabilitare la tematizzazione sui controlli, in questo modo, per esempio:Disabilitare il tema di controlli specifici?

SetWindowTheme(Button1.Handle, nil, nil); 

Questo funziona su un bel po 'dei controlli, tuttavia non funzionerà su alcuni controlli come TBitBtn o TSpeedButton. Penso che questo sia dovuto al fatto che TBitBtn e TSpeedButton non sono controlli di Windows, ma quelli personalizzati?

Potrebbero esserci anche altri controlli che non funzionano, quindi speravo che qualcuno potesse condividere una soluzione o un'alternativa per raggiungere questo obiettivo?

Desidero che alcuni dei controlli non contengano alcun argomento, ad esempio mostreranno un tema classico mentre il resto dei controlli non sarà interessato.

Grazie.

+0

Hai già letto la fonte per il disegno di questi controlli VCL. –

+0

In StdCtrls.pas vedo che TButton è di discendenza TWinControl, e in Buttons.pas credo che TBitBtn e TSpeedButton siano classi personalizzate. Anche a me sembra tutto un po 'complicato! –

+2

'TSpeedButton' è un' TGraphicControl' che non ha comunque un handle. Potresti sovrascrivere 'Paint' di' TspeedButton' e 'CNDrawItem' di' TBitBtn' ... Perché XE non ha fornito una sorta di 'UseThemes' per i controlli personalizzati? non so ... – kobik

risposta

13

L'analisi è corretta. SetWindowTheme funziona per i controlli finestra ma TSpeedButton e TBitBtn sono controlli non abilitati.

In XE, dalla mia scansione rapida, sembra che la maggior parte dei controlli chiama Themes.ThemeControl per determinare se disegnare o meno a tema. Quindi la soluzione semplice è sostituire quella routine con la logica che controlli. Dal momento che non fornisce punti di estensione, è necessario agganciarlo. Come questo:

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer); 
var 
    OldProtect: DWORD; 
begin 
    if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then 
    begin 
    Move(NewCode, Address^, Size); 
    FlushInstructionCache(GetCurrentProcess, Address, Size); 
    VirtualProtect(Address, Size, OldProtect, @OldProtect); 
    end; 
end; 

type 
    PInstruction = ^TInstruction; 
    TInstruction = packed record 
    Opcode: Byte; 
    Offset: Integer; 
    end; 

procedure RedirectProcedure(OldAddress, NewAddress: Pointer); 
var 
    NewCode: TInstruction; 
begin 
    NewCode.Opcode := $E9;//jump relative 
    NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode); 
    PatchCode(OldAddress, NewCode, SizeOf(NewCode)); 
end; 

function MyThemeControl(AControl: TControl): Boolean; 
begin 
    Result := False; 
    if AControl = nil then exit; 
    if AControl is TSpeedButton then exit; 
    if AControl is TBitBtn then exit; 
    Result := (not (csDesigning in AControl.ComponentState) and ThemeServices.ThemesEnabled) or 
      ((csDesigning in AControl.ComponentState) and (AControl.Parent <> nil) and 
      (ThemeServices.ThemesEnabled and not UnthemedDesigner(AControl.Parent))); 
end; 

initialization 
    RedirectProcedure(@Themes.ThemeControl, @MyThemeControl); 

Così com'è, questo non funzionerà con i pacchetti di runtime, ma è abbastanza facile per estendere il codice per lavorare con i pacchetti.

+0

sembra piuttosto complesso! –

+1

@blobby Se si desidera modificare questo comportamento e non modificare il codice vcl o creare sottoclassi e copiare/incollare il codice VCL, questa è la risposta. –

+0

E una risposta ben scritta molto tecnica è David, anche se non ne capisco niente! Mi sconvolge il modo in cui tu e gli altri potete trovare un codice come questo - Incredibile :) –

5

Se si guarda il codice sorgente per TBitBtn (in particolare, TBitBtn.DrawItem), si vede che è disegnato manualmente nel codice sorgente Delphi. Utilizza l'API di temi visivi di Windows per disegnare il pulsante (ThemeServices.Draw*) nel tema corrente, se i temi sono abilitati. In caso contrario, utilizza le funzioni API di Windows vecchio stile per disegnare i controlli, ad esempio Rectangle e DrawFrameControl. Penso che devi modificare il codice sorgente del controllo per aggirare questo comportamento.

+0

Grazie per l'informazione Andreas. Comunque preferirei non voler modificare il codice sorgente di Delphi. –

Problemi correlati