使用 Windows API 绘制 Button 时,我们是否负责绘制标题?
Are we responsible for drawing the caption when using Windows API to draw a Button?
简介
我一直在玩 Windows API 和 Parts and States 并了解如何将控件绘制到 canvas.
经过大量的反复试验,我设法想出了这个程序,它将在 canvas:
上绘制一个按钮
type
TButtonState = (bsDefault, bsDisabled, bsHot, bsNormal, bsPressed);
procedure DrawButton(ACanvas: TCanvas; X, Y, AWidth, AHeight: Integer;
AFont: TFont; Caption: string; ButtonState: TButtonState);
var
Size: TSize;
R: TRect;
H: HTHEME;
begin
Size.cx := AWidth;
Size.cy := AHeight;
R := Rect(X, Y, X + AWidth, Y + AHeight);
if Winapi.uxTheme.UseThemes then
begin
H := OpenThemeData(0, 'BUTTON');
if H <> 0 then
try
ACanvas.Brush.Style := bsClear;
if AFont <> nil then
begin
ACanvas.Font.Assign(AFont);
end;
case ButtonState of
bsDefault:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DEFAULTED, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DEFAULTED, R, nil);
end;
bsDisabled:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, R, nil);
//ACanvas.Font.Color := [=12=]838383; //todo get actual disabled font color
end;
bsHot:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_HOT, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_HOT, R, nil);
end;
bsNormal:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_NORMAL, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_NORMAL, R, nil);
end;
bsPressed:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_PRESSED, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_PRESSED, R, nil);
end;
end;
// draw the button caption
DrawText(ACanvas.Handle, PChar(Caption), Length(Caption), R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
finally
CloseThemeData(H);
end
end
else
begin
// draw button in classic theme?
end;
end;
如果我这样调用该过程:
procedure TForm1.FormPaint(Sender: TObject);
begin
DrawButton(Image1.Canvas, 10, 10, 75, 25, Form1.Font, 'Normal', bsNormal);
DrawButton(Image1.Canvas, 10, 40, 75, 25, Form1.Font, 'Default', bsDefault);
DrawButton(Image1.Canvas, 10, 70, 75, 25, Form1.Font, 'Disabled', bsDisabled);
DrawButton(Image1.Canvas, 10, 100, 75, 25, Form1.Font, 'Hot', bsHot);
DrawButton(Image1.Canvas, 10, 130, 75, 25, Form1.Font, 'Pressed', bsPressed);
end;
结果如我所料,看起来就像任何 Windows 按钮控件一样:
问题
当我尝试这个过程时,我很快意识到按钮没有标题,而且除了自己在按钮顶部绘制之外,我看不到添加标题的方法。正如您所看到的,"Disabled" 按钮仍然显示默认字体颜色,禁用的控件通常有不同的颜色以帮助显示该控件已禁用。标准 Windows 主题下禁用的字体颜色是 [=14=]838383
(使用屏幕颜色选择器找到),但硬编码值绝不是一个好主意,因为这些值通常对每个主题都是唯一的。
我的问题分为几个部分,当使用 Windows API 绘制按钮时,我们必须自己手动绘制标题吗?如果是这样,我如何确保绘制正确的字体名称、样式和大小等以确保按钮与系统宽绘制的按钮相同?
奖金
未启用主题时,我应该如何以经典 Windows 样式绘制按钮?
你应该自己画文字。您也可以使用主题 api 来绘制文本。例如:
...
bsDisabled:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, R, nil);
DrawThemeText(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, PChar(Caption),
Length(Caption), DT_CENTER or DT_VCENTER or DT_SINGLELINE, 0, R);
//ACanvas.Font.Color := [=10=]838383; //todo get actual disabled font color
end;
...
由于您会以适当的状态调用 api,因此请将调用包含在 case 分支中并删除对 DrawText 的调用。
经典风格可以使用DrawFrameControl。例如:
DrawFrameControl(ACanvas.Handle, R, DFC_BUTTON, DFCS_BUTTONPUSH);
简介
我一直在玩 Windows API 和 Parts and States 并了解如何将控件绘制到 canvas.
经过大量的反复试验,我设法想出了这个程序,它将在 canvas:
上绘制一个按钮type
TButtonState = (bsDefault, bsDisabled, bsHot, bsNormal, bsPressed);
procedure DrawButton(ACanvas: TCanvas; X, Y, AWidth, AHeight: Integer;
AFont: TFont; Caption: string; ButtonState: TButtonState);
var
Size: TSize;
R: TRect;
H: HTHEME;
begin
Size.cx := AWidth;
Size.cy := AHeight;
R := Rect(X, Y, X + AWidth, Y + AHeight);
if Winapi.uxTheme.UseThemes then
begin
H := OpenThemeData(0, 'BUTTON');
if H <> 0 then
try
ACanvas.Brush.Style := bsClear;
if AFont <> nil then
begin
ACanvas.Font.Assign(AFont);
end;
case ButtonState of
bsDefault:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DEFAULTED, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DEFAULTED, R, nil);
end;
bsDisabled:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, R, nil);
//ACanvas.Font.Color := [=12=]838383; //todo get actual disabled font color
end;
bsHot:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_HOT, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_HOT, R, nil);
end;
bsNormal:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_NORMAL, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_NORMAL, R, nil);
end;
bsPressed:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_PRESSED, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_PRESSED, R, nil);
end;
end;
// draw the button caption
DrawText(ACanvas.Handle, PChar(Caption), Length(Caption), R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
finally
CloseThemeData(H);
end
end
else
begin
// draw button in classic theme?
end;
end;
如果我这样调用该过程:
procedure TForm1.FormPaint(Sender: TObject);
begin
DrawButton(Image1.Canvas, 10, 10, 75, 25, Form1.Font, 'Normal', bsNormal);
DrawButton(Image1.Canvas, 10, 40, 75, 25, Form1.Font, 'Default', bsDefault);
DrawButton(Image1.Canvas, 10, 70, 75, 25, Form1.Font, 'Disabled', bsDisabled);
DrawButton(Image1.Canvas, 10, 100, 75, 25, Form1.Font, 'Hot', bsHot);
DrawButton(Image1.Canvas, 10, 130, 75, 25, Form1.Font, 'Pressed', bsPressed);
end;
结果如我所料,看起来就像任何 Windows 按钮控件一样:
问题
当我尝试这个过程时,我很快意识到按钮没有标题,而且除了自己在按钮顶部绘制之外,我看不到添加标题的方法。正如您所看到的,"Disabled" 按钮仍然显示默认字体颜色,禁用的控件通常有不同的颜色以帮助显示该控件已禁用。标准 Windows 主题下禁用的字体颜色是 [=14=]838383
(使用屏幕颜色选择器找到),但硬编码值绝不是一个好主意,因为这些值通常对每个主题都是唯一的。
我的问题分为几个部分,当使用 Windows API 绘制按钮时,我们必须自己手动绘制标题吗?如果是这样,我如何确保绘制正确的字体名称、样式和大小等以确保按钮与系统宽绘制的按钮相同?
奖金
未启用主题时,我应该如何以经典 Windows 样式绘制按钮?
你应该自己画文字。您也可以使用主题 api 来绘制文本。例如:
...
bsDisabled:
begin
GetThemePartSize(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, nil, TS_DRAW, Size);
DrawThemeBackground(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, R, nil);
DrawThemeText(H, ACanvas.Handle, BP_PUSHBUTTON, PBS_DISABLED, PChar(Caption),
Length(Caption), DT_CENTER or DT_VCENTER or DT_SINGLELINE, 0, R);
//ACanvas.Font.Color := [=10=]838383; //todo get actual disabled font color
end;
...
由于您会以适当的状态调用 api,因此请将调用包含在 case 分支中并删除对 DrawText 的调用。
经典风格可以使用DrawFrameControl。例如:
DrawFrameControl(ACanvas.Handle, R, DFC_BUTTON, DFCS_BUTTONPUSH);