2012-03-06 8 views
13

Kiedy próbuję wyłączyć przycisk na stylizowany VCL z użyciem następujących urządzeń linii koduWyłączenie TButton problem na VCL stylizowany formie

TButton(Sender).enabled:= False; 

uzyskać ten wynik (przycisk wyłączone przy starcie)

enter image description here

zamiast tego !! (Przycisk wyłączony w czasie projektowania)

enter image description here

To naprawdę mylące, aby mieć dwa lub więcej przycisków z tego samego koloru obok siebie, jeden jest wyłączony, a drugi jest włączona

+0

Czy to właśnie pokazuje ten konkretny styl, czy też myślisz, że jest jakiś błąd? Jeśli pierwszym obrazem jest wyłączony przycisk, jaki jest drugi obraz? –

+2

@DavidHeffernan pierwszy obraz jest wyłączany przez kod, drugi jest wyłączony z Inspektora obiektów (czas projektowania), myślę, że to błąd! – Raul

+0

co powiesz na "przemalowywanie" po wyłączeniu przycisku w czasie wykonywania? – teran

Odpowiedz

16

Przyczyna tego problemu znajduje się w metodzie malowania klasy haczyka TButtonStyleHook (w klasie Vcl.StdCtrls).

Zlokalizuj ten kod w metodzie

if FPressed then 
    Details := StyleServices.GetElementDetails(tbPushButtonPressed) 
else if MouseInControl then //this condition is triggered even if the button is disabled 
    Details := StyleServices.GetElementDetails(tbPushButtonHot) 
else if Focused then //this condition is triggered even if the button is disabled 
    Details := StyleServices.GetElementDetails(tbPushButtonDefaulted) 
else if Control.Enabled then 
    Details := StyleServices.GetElementDetails(tbPushButtonNormal) 
else 
    Details := StyleServices.GetElementDetails(tbPushButtonDisabled); 

I zastąpić dla tego kodu

if FPressed then 
    Details := StyleServices.GetElementDetails(tbPushButtonPressed) 
else if MouseInControl and Control.Enabled then 
    Details := StyleServices.GetElementDetails(tbPushButtonHot) 
else if Focused and Control.Enabled then 
    Details := StyleServices.GetElementDetails(tbPushButtonDefaulted) 
else if Control.Enabled then 
    Details := StyleServices.GetElementDetails(tbPushButtonNormal) 
else 
    Details := StyleServices.GetElementDetails(tbPushButtonDisabled); 

Innym rozwiązaniem jest przepisać hak styl do TButton:

sprawdzić ten kod oparty w ten artykuł Fixing a VCL Style bug in the TButton component. Zaletą tego kodu jest to, że naprawiasz dwa problemy: 103708 i 103962.

Uses 
Winapi.CommCtrl, 
Vcl.Themes, 
Vcl.Styles; 

type 
    TCustomButtonH=class(TCustomButton); 

    //we need this helper to access some strict private fields 
    TButtonStyleHookHelper = class Helper for TButtonStyleHook 
    protected 
    function Pressed : Boolean; 
    function DropDown: Boolean; 
    end; 

    //to avoid writting a lot of extra code we are to use TButtonStyleHook class and override the paint method 
    TButtonStyleHookFix = class(TButtonStyleHook) 
    protected 
    procedure Paint(Canvas: TCanvas); override; 
    end; 


{ TButtonStyleHookFix } 

procedure TButtonStyleHookFix.Paint(Canvas: TCanvas); 
var 
    LDetails   : TThemedElementDetails; 
    DrawRect   : TRect; 
    pbuttonImagelist : BUTTON_IMAGELIST; 
    IW, IH, IY  : Integer; 
    LTextFormatFlags : TTextFormatFlags; 
    ThemeTextColor : TColor; 
    Buffer   : string; 
    BufferLength  : Integer; 
    SaveIndex   : Integer; 
    X, Y, I   : Integer; 
    BCaption   : String; 
begin 

    if StyleServices.Available then 
    begin 
    BCaption := Text; 

    if Pressed then 
     LDetails := StyleServices.GetElementDetails(tbPushButtonPressed) 
    else 
    if MouseInControl and Control.Enabled then 
     LDetails := StyleServices.GetElementDetails(tbPushButtonHot) 
    else 
    if Focused and Control.Enabled then 
     LDetails := StyleServices.GetElementDetails(tbPushButtonDefaulted) 
    else 
    if Control.Enabled then 
     LDetails := StyleServices.GetElementDetails(tbPushButtonNormal) 
    else 
     LDetails := StyleServices.GetElementDetails(tbPushButtonDisabled); 

    DrawRect := Control.ClientRect; 
    StyleServices.DrawElement(Canvas.Handle, LDetails, DrawRect); 

    if Button_GetImageList(handle, pbuttonImagelist) and (pbuttonImagelist.himl <> 0) and ImageList_GetIconSize(pbuttonImagelist.himl, IW, IH) then 
    begin 
     if (GetWindowLong(Handle, GWL_STYLE) and BS_COMMANDLINK) = BS_COMMANDLINK then 
     IY := DrawRect.Top + 15 
     else 
     IY := DrawRect.Top + (DrawRect.Height - IH) div 2; 

     //here the image is drawn properly according to the ImageAlignment value 
     case TCustomButton(Control).ImageAlignment of 
     iaLeft : 
        begin 
        ImageList_Draw(pbuttonImagelist.himl, 0, Canvas.Handle, DrawRect.Left + 3, IY, ILD_NORMAL); 
        Inc(DrawRect.Left, IW + 3); 
        end; 
     iaRight : 
        begin 
        ImageList_Draw(pbuttonImagelist.himl, 0, Canvas.Handle, DrawRect.Right - IW -3, IY, ILD_NORMAL); 
        Dec(DrawRect.Right, IW - 3); 
        end; 

     iaCenter: 
        begin 
        ImageList_Draw(pbuttonImagelist.himl, 0, Canvas.Handle, (DrawRect.Right - IW) div 2, IY, ILD_NORMAL); 
        end; 


     iaTop : 
        begin 
        ImageList_Draw(pbuttonImagelist.himl, 0, Canvas.Handle, (DrawRect.Right - IW) div 2, 3, ILD_NORMAL); 
        end; 


     iaBottom: 
        begin 
        ImageList_Draw(pbuttonImagelist.himl, 0, Canvas.Handle, (DrawRect.Right - IW) div 2, (DrawRect.Height - IH) - 3, ILD_NORMAL); 
        end; 

     end; 


    end; 

    if (GetWindowLong(Handle, GWL_STYLE) and BS_COMMANDLINK) = BS_COMMANDLINK then 
    begin 
     if pbuttonImagelist.himl = 0 then 
     Inc(DrawRect.Left, 35); 

     Inc(DrawRect.Top, 15); 
     Inc(DrawRect.Left, 5); 
     Canvas.Font := TCustomButtonH(Control).Font; 
     Canvas.Font.Style := []; 
     Canvas.Font.Size := 12; 
     LTextFormatFlags := TTextFormatFlags(DT_LEFT); 
     if StyleServices.GetElementColor(LDetails, ecTextColor, ThemeTextColor) then 
     Canvas.Font.Color := ThemeTextColor; 
     StyleServices.DrawText(Canvas.Handle, LDetails, BCaption, DrawRect, LTextFormatFlags, Canvas.Font.Color); 
     SetLength(Buffer, Button_GetNoteLength(Handle) + 1); 
     if Length(Buffer) <> 0 then 
     begin 
     BufferLength := Length(Buffer); 
     if Button_GetNote(Handle, PChar(Buffer), BufferLength) then 
     begin 
      LTextFormatFlags := TTextFormatFlags(DT_LEFT or DT_WORDBREAK); 
      Inc(DrawRect.Top, Canvas.TextHeight('Wq') + 2); 
      Canvas.Font.Size := 8; 
      StyleServices.DrawText(Canvas.Handle, LDetails, Buffer, DrawRect, 
      LTextFormatFlags, Canvas.Font.Color); 
     end; 
     end; 

     if pbuttonImagelist.himl = 0 then 
     begin 
     if Pressed then 
      LDetails := StyleServices.GetElementDetails(tbCommandLinkGlyphPressed) 
     else if MouseInControl then 
      LDetails := StyleServices.GetElementDetails(tbCommandLinkGlyphHot) 
     else if Control.Enabled then 
      LDetails := StyleServices.GetElementDetails(tbCommandLinkGlyphNormal) 
     else 
      LDetails := StyleServices.GetElementDetails(tbCommandLinkGlyphDisabled); 
     DrawRect.Right := 35; 
     DrawRect.Left := 3; 
     DrawRect.Top := 10; 
     DrawRect.Bottom := DrawRect.Top + 32; 
     StyleServices.DrawElement(Canvas.Handle, LDetails, DrawRect); 
     end; 

    end 
    else 
    if (GetWindowLong(Handle, GWL_STYLE) and BS_SPLITBUTTON) = BS_SPLITBUTTON then 
    begin 
     Dec(DrawRect.Right, 15); 
     DrawControlText(Canvas, LDetails, Text, DrawRect, DT_VCENTER or DT_CENTER); 
     if DropDown then 
     begin 
     LDetails := StyleServices.GetElementDetails(tbPushButtonPressed); 
     SaveIndex := SaveDC(Canvas.Handle); 
     try 
      IntersectClipRect(Canvas.Handle, Control.Width - 15, 0, 
      Control.Width, Control.Height); 
      DrawRect := Rect(Control.Width - 30, 0, Control.Width, Control.Height); 
      StyleServices.DrawElement(Canvas.Handle, LDetails, DrawRect); 
     finally 
      RestoreDC(Canvas.Handle, SaveIndex); 
     end; 
     end; 

     with Canvas do 
     begin 
     Pen.Color := StyleServices.GetSystemColor(clBtnShadow); 
     MoveTo(Control.Width - 15, 3); 
     LineTo(Control.Width - 15, Control.Height - 3); 
     if Control.Enabled then 
      Pen.Color := StyleServices.GetSystemColor(clBtnHighLight) 
     else 
      Pen.Color := Font.Color; 
     MoveTo(Control.Width - 14, 3); 
     LineTo(Control.Width - 14, Control.Height - 3); 
     Pen.Color := Font.Color; 
     X := Control.Width - 8; 
     Y := Control.Height div 2 + 1; 
     for i := 3 downto 0 do 
     begin 
      MoveTo(X - I, Y - I); 
      LineTo(X + I + 1, Y - I); 
     end; 
     end; 

    end 
    else 
    begin 
     //finally the text is aligned and drawn depending of the value of the ImageAlignment property 
     case TCustomButton(Control).ImageAlignment of 
     iaLeft, 
     iaRight, 
     iaCenter : DrawControlText(Canvas, LDetails, BCaption, DrawRect, DT_VCENTER or DT_CENTER); 
     iaBottom : DrawControlText(Canvas, LDetails, BCaption, DrawRect, DT_TOP or DT_CENTER); 
     iaTop : DrawControlText(Canvas, LDetails, BCaption, DrawRect, DT_BOTTOM or DT_CENTER); 
     end; 
    end; 
    end; 
end; 

{ TButtonStyleHookHelper } 

function TButtonStyleHookHelper.DropDown: Boolean; 
begin 
    Result:=Self.FDropDown; 
end; 

function TButtonStyleHookHelper.Pressed: Boolean; 
begin 
    Result:=Self.FPressed; 
end; 


initialization 
TStyleManager.Engine.RegisterStyleHook(TButton, TButtonStyleHookFix); 
+1

+1 dobrze zrobione. Wiedziałem, że nasz rezydentny guru stylu VCL utknie w tym. Nie mogę uwierzyć, jak kiepski jest kod VCL. –

+0

podczas pisania poprawki jest po prostu o wiele czystsze, aby obsłużyć najpierw warunek Control.Enabled. Oznacza to, że musisz przetestować tę właściwość tylko raz. –

+2

Potrzebuje więcej niż "+1" Embarcadero powinien zapłacić obojgu za dodatkowe wysiłki :) – Raul

11

To wyraźnie błąd w VCL. Problem polega na tym, że modyfikowanie właściwości przycisku z programu obsługi zdarzenia dołączonego do tego przycisku nie zmienia wyglądu przycisku. Zachowanie przycisku zostało zmienione (nie można go kliknąć, jeśli ustawisz w ten sposób Enabled na False), ale efekty wizualne tego nie wskazują.

Przedłożyłem QC#103962 i bez wątpienia przyszłe aktualizacje rozwiążą problem. W międzyczasie proponuję następujące rozwiązania:

procedure TMyForm.Button1Click(Sender: TObject); 
begin 
    Button1.Enabled := False; 
    Button1.Perform(CM_RECREATEWND, 0, 0); 
end; 

To zmusi okno obsłużyć przycisku, który ma być odtworzony i to wydaje się być na tyle, aby uzyskać efekty wizualne posortowane. Są prawdopodobnie alternatywne sposoby obejścia tego, ale to wszystko, co do tej pory znalazłem.

+0

Świetnie !! Wielkie dzięki, Dave, twoja odpowiedź rozwiązała mój dotychczasowy problem – Raul

+0

+1 za zgłoszenie QC - trzymajmy się, chłopaki! –

+0

Dzięki, właśnie wpadłem na ten błąd i mogłem szybko przejść do tego obejścia. Zagłosowano również na qc. –