Delphi 我们是否负责在使用Windows API绘制按钮时绘制标题? 介绍

Delphi 我们是否负责在使用Windows API绘制按钮时绘制标题? 介绍,delphi,winapi,delphi-xe7,Delphi,Winapi,Delphi Xe7,我一直对WindowsAPI有点兴趣,并看到了如何将控件绘制到画布上 经过反复试验,我终于想出了这个程序,可以在画布上画一个按钮: type TButtonState = (bsDefault, bsDisabled, bsHot, bsNormal, bsPressed); procedure DrawButton(ACanvas: TCanvas; X, Y, AWidth, AHeight: Integer; AFont: TFont; Caption: string; Butt

我一直对WindowsAPI有点兴趣,并看到了如何将控件绘制到画布上

经过反复试验,我终于想出了这个程序,可以在画布上画一个按钮:

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 := $00838383; //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;
结果就是我所期望的,就像任何Windows按钮控件一样:

问题: 当我在玩这个程序的时候,我很快意识到这个按钮没有标题,除了自己在按钮上画标题之外,我看不到添加标题的方法。如您所见,“Disabled”(已禁用)按钮仍显示默认字体颜色,禁用的控件通常具有不同的颜色以帮助显示控件已禁用。标准Windows主题下禁用的字体颜色是
$00838383
(使用屏幕颜色选择器找到),但硬编码值从来不是一个好主意,因为这些值通常对每个主题都是唯一的

我的问题有几个部分,当使用WinOWSAPI绘制按钮时,我们需要自己手动绘制标题吗?如果是,我如何确保绘制的字体名称、样式和大小等正确,以确保按钮与系统范围内绘制的按钮相同

奖金
未启用主题时,如何以经典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 := $00838383; //todo get actual disabled font color
        end;
...
因为您将使用适当的状态调用api,所以将调用包括在case分支中,并删除对DrawText的调用

您可以将DrawFrameControl用于经典样式。例如:

DrawFrameControl(ACanvas.Handle, R, DFC_BUTTON, DFCS_BUTTONPUSH);

在您的用例中,您必须自己绘制按钮(非常罕见!)还是调用WinAPI来创建按钮(然后由Windows管理而无需担心字体样式)可以?后者是使用WinAPI的正常方式,如果您没有任何特殊要求,我建议使用后者。如果未启用主题,则必须使用
DrawText()
而不是
DrawThemeText()
。还有一个用于绘制禁用文本的
GrayString()
函数。还可以使用GetSysColor和COLOR\u GRAYTEXT来获取经典样式的禁用文本的颜色。这正是我所需要的,谢谢大家提供的有用信息。
DrawFrameControl(ACanvas.Handle, R, DFC_BUTTON, DFCS_BUTTONPUSH);