当我尝试使用以下代码行禁用样式化 VCL 上的按钮时
TButton(Sender).enabled:= False;
我得到了这个结果(在运行时禁用了按钮)
而不是这个!!(在设计时禁用按钮)
将两个或多个具有相同颜色的按钮放在一起真的很混乱,一个被禁用,另一个被启用
当我尝试使用以下代码行禁用样式化 VCL 上的按钮时
TButton(Sender).enabled:= False;
我得到了这个结果(在运行时禁用了按钮)
而不是这个!!(在设计时禁用按钮)
将两个或多个具有相同颜色的按钮放在一起真的很混乱,一个被禁用,另一个被启用
此问题的原因位于TButtonStyleHook
(在 Vcl.StdCtrls 单元中)样式挂钩类的 Paint 方法中。
在方法中找到此代码
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);
并替换此代码
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);
另一种选择是重写 TButton 的 Style 钩子:
根据这篇文章Fixing a VCL Style bug in the TButton component检查此代码。该代码的优点是您正在修复两个问题103708 和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);
这显然是 VCL 中的一个错误。问题似乎是Enabled
从附加到该按钮的事件处理程序修改按钮的属性不会改变按钮的视觉外观。按钮的行为已更改(如果设置Enabled
为False
这种方式,则无法单击它),但视觉效果并未表明。
我提交了QC#103962,毫无疑问,未来的更新将解决这个问题。同时,我提供以下解决方法:
procedure TMyForm.Button1Click(Sender: TObject);
begin
Button1.Enabled := False;
Button1.Perform(CM_RECREATEWND, 0, 0);
end;
这将强制重新创建按钮的窗口句柄,这似乎足以对视觉效果进行排序。可能有其他方法可以解决这个问题,但这是我迄今为止发现的全部。