我已经为您创建了一个组件。由于我没有太多的组件编写经验,所以它不是很酷,请原谅 :)
现在有两个可用的组件:
以下属性对这两个组件都有效:
- ProgressMin - 进度条的最小值
- ProgressMax - 进度条的最大值
- ProgressValue - 当前进度条的值
- ProgressAlpha - 进度条的透明度(范围为0-175,其中175是最大可见度)
- ProgressColor - 进度条的颜色
- ProgressColored - 启用ProgressColor的标志
- ProgressMargins - 按钮内边框与进度条外边框之间的边距
以下属性仅对TProgressGlyphButton
有效:
- Images - 包含按钮状态图像(禁用、默认、正常、热、按下)的图像列表
- 如果没有足够的图像来覆盖所有状态,则只绘制第一个图像以覆盖所有状态
- ImageTop - 图标的垂直缩进,仅在ImageAlign设置为iaCustom时有效
- ImageLeft - 图标的水平缩进,仅在ImageAlign设置为iaCustom时有效
- ImageAlign - 图标对齐样式
- iaLeft将图标对齐到左侧,并将其缩进垂直图标居中的结果
- iaRight将图标对齐到右侧,并将其缩进垂直图标居中的结果
- iaCustom允许您手动指定图标坐标(请参见上面的属性)
Font
属性影响文本渲染,因此您可以更改字体样式、颜色或其他属性。请注意,此组件仅能在启用Windows主题的情况下使用。
这两个组件都包含演示和源代码;由于帖子长度限制,我无法在此处发布更新后的代码。因此,我在此留下原始代码。
unit ProgressButton;
interface
uses Windows, Messages, Classes, Controls, Forms, Graphics, StdCtrls,
SysUtils, ExtCtrls, CommCtrl, UxTheme, Themes;
type
TButtonState = (bsDisabled, bsDefault, bsNormal, bsButtonHot, bsPressed);
TBufferType = (btProgress, btButton, btCaption);
TBufferTypes = set of TBufferType;
TProgressButton = class(TButton)
private
FDrawBuffer: TBitmap;
FButtonBuffer: TBitmap;
FProgressBuffer: TBitmap;
FProgressMin: Integer;
FProgressMax: Integer;
FProgressValue: Integer;
FProgressAlpha: Integer;
FProgressColor: TColor;
FProgressColored: Boolean;
FProgressMargins: Integer;
FProgressSpacing: Integer;
FButtonState: TButtonState;
FFocusInControl: Boolean;
FMouseInControl: Boolean;
procedure PrepareButtonBuffer;
procedure PrepareProgressBuffer;
procedure PrepareDrawBuffers(const BufferTypes: TBufferTypes);
procedure SetProgressMin(Value: Integer);
procedure SetProgressMax(Value: Integer);
procedure SetProgressValue(Value: Integer);
procedure SetProgressAlpha(Value: Integer);
procedure SetProgressColor(Value: TColor);
procedure SetProgressColored(Value: Boolean);
procedure SetProgressMargins(Value: Integer);
function GetButtonState(const ItemState: UINT): TButtonState;
procedure CNDrawItem(var Msg: TWMDrawItem); message CN_DRAWITEM;
procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
procedure CMFontChanged(var Msg: TMessage); message CM_FONTCHANGED;
procedure CMTextChanged(var Msg: TMessage); message CM_TEXTCHANGED;
procedure WMLButtonDblClk(var Msg: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
procedure WMWindowPosChanged(var Msg: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED;
protected
procedure Loaded; override;
procedure SetButtonStyle(Value: Boolean); override;
procedure CreateParams(var Params: TCreateParams); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property ProgressMin: Integer read FProgressMin write SetProgressMin default 0;
property ProgressMax: Integer read FProgressMax write SetProgressMax default 100;
property ProgressValue: Integer read FProgressValue write SetProgressValue default 0;
property ProgressAlpha: Integer read FProgressAlpha write SetProgressAlpha default 75;
property ProgressColor: TColor read FProgressColor write SetProgressColor default $00804000;
property ProgressColored: Boolean read FProgressColored write SetProgressColored default False;
property ProgressMargins: Integer read FProgressMargins write SetProgressMargins default 1;
end;
procedure Register;
implementation
constructor TProgressButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
if csDesigning in ComponentState then
if not ThemeServices.ThemesEnabled then
begin
raise EInvalidOperation.Create(
'Hi, I''m the ProgressButton control, but I cannot be loaded because' + sLineBreak +
'you don''t have the Windows Themes enabled and my initial developer' + sLineBreak +
'was so lazy to paint me without them.');
end;
Width := 185;
Height := 25;
FProgressMin := 0;
FProgressMax := 100;
FProgressValue := 0;
FProgressAlpha := 75;
FProgressColor := $00804000;
FProgressColored := False;
FProgressMargins := 1;
FButtonState := bsNormal;
if Win32MajorVersion >= 6 then
FProgressSpacing := 1
else
FProgressSpacing := 2;
FDrawBuffer := TBitmap.Create;
FDrawBuffer.PixelFormat := pf32Bit;
FButtonBuffer := TBitmap.Create;
FButtonBuffer.PixelFormat := pf32Bit;
FProgressBuffer := TBitmap.Create;
FProgressBuffer.PixelFormat := pf32Bit;
end;
destructor TProgressButton.Destroy;
begin
inherited Destroy;
FDrawBuffer.Free;
FButtonBuffer.Free;
FProgressBuffer.Free;
end;
procedure TProgressButton.PrepareButtonBuffer;
var
ThemedButton: TThemedButton;
ThemedDetails: TThemedElementDetails;
begin
ThemedButton := tbButtonDontCare;
case FButtonState of
bsDisabled: ThemedButton := tbPushButtonDisabled;
bsDefault: ThemedButton := tbPushButtonDefaulted;
bsNormal: ThemedButton := tbPushButtonNormal;
bsButtonHot: ThemedButton := tbPushButtonHot;
bsPressed: ThemedButton := tbPushButtonPressed;
end;
PerformEraseBackground(Self, FButtonBuffer.Canvas.Handle);
ThemedDetails := ThemeServices.GetElementDetails(ThemedButton);
ThemeServices.DrawElement(FButtonBuffer.Canvas.Handle, ThemedDetails, ClientRect, nil);
end;
procedure TProgressButton.PrepareProgressBuffer;
var
ProgressBar: TRect;
ProgressChunk: TRect;
ThemedDetails: TThemedElementDetails;
procedure ColorizeBitmap(const Bitmap: TBitmap; const Color: TColor);
type
PPixelRec = ^TPixelRec;
TPixelRec = packed record
B: Byte;
G: Byte;
R: Byte;
Alpha: Byte;
end;
var
X: Integer;
Y: Integer;
R: Integer;
G: Integer;
B: Integer;
Gray: Byte;
Pixel: PPixelRec;
begin
R := GetRValue(Color);
G := GetGValue(Color);
B := GetBValue(Color);
for Y := ProgressChunk.Top to ProgressChunk.Bottom - 1 do
begin
Pixel := Bitmap.ScanLine[Y];
Inc(Pixel, FProgressMargins + FProgressSpacing);
for X := ProgressChunk.Left to ProgressChunk.Right - 1 do
begin
Gray := Round((0.299 * Pixel.R) + (0.587 * Pixel.G) + (0.114 * Pixel.B));
if (Win32MajorVersion >= 6) or ((Win32MajorVersion < 6) and (Gray < 240)) then
begin
Pixel.R := MulDiv(R, Gray, 255);
Pixel.G := MulDiv(G, Gray, 255);
Pixel.B := MulDiv(B, Gray, 255);
end;
Inc(Pixel);
end;
end;
end;
begin
ProgressBar := Rect(
ClientRect.Left + FProgressMargins,
ClientRect.Top + FProgressMargins,
ClientRect.Right - FProgressMargins,
ClientRect.Bottom - FProgressMargins);
ProgressChunk := Rect(
ProgressBar.Left + FProgressSpacing,
ProgressBar.Top + FProgressSpacing,
ProgressBar.Left + FProgressSpacing + Trunc((FProgressValue - FProgressMin) / (FProgressMax - FProgressMin) * (ProgressBar.Right - ProgressBar.Left - (2 * FProgressSpacing))),
ProgressBar.Bottom - FProgressSpacing);
PerformEraseBackground(Self, FProgressBuffer.Canvas.Handle);
ThemedDetails := ThemeServices.GetElementDetails(tpBar);
ThemeServices.DrawElement(FProgressBuffer.Canvas.Handle, ThemedDetails, ProgressBar, nil);
ThemedDetails := ThemeServices.GetElementDetails(tpChunk);
ThemeServices.DrawElement(FProgressBuffer.Canvas.Handle, ThemedDetails, ProgressChunk, nil);
if FProgressColored then
ColorizeBitmap(FProgressBuffer, FProgressColor);
end;
procedure TProgressButton.PrepareDrawBuffers(const BufferTypes: TBufferTypes);
var
TextBounds: TRect;
BlendFunction: TBlendFunction;
begin
if (csLoading in ComponentState) or (not Assigned(Parent)) then
Exit;
FDrawBuffer.Width := Width;
FDrawBuffer.Height := Height;
FButtonBuffer.Width := Width;
FButtonBuffer.Height := Height;
FProgressBuffer.Width := Width;
FProgressBuffer.Height := Height;
if btProgress in BufferTypes then
PrepareProgressBuffer;
if btButton in BufferTypes then
PrepareButtonBuffer;
BitBlt(FDrawBuffer.Canvas.Handle, 0, 0, Width, Height, FProgressBuffer.Canvas.Handle, 0, 0, SRCCOPY);
BlendFunction.BlendOp := AC_SRC_OVER;
BlendFunction.BlendFlags := 0;
BlendFunction.SourceConstantAlpha := 255 - FProgressAlpha;
BlendFunction.AlphaFormat := 0;
AlphaBlend(FDrawBuffer.Canvas.Handle, 0, 0, Width, Height, FButtonBuffer.Canvas.Handle, 0, 0, Width, Height,
BlendFunction);
if Caption <> '' then
begin
TextBounds := ClientRect;
if Enabled then
FDrawBuffer.Canvas.Font.Color := Font.Color
else
FDrawBuffer.Canvas.Font.Color := clGrayText;
SelectObject(FDrawBuffer.Canvas.Handle, Font.Handle);
SetBkMode(FDrawBuffer.Canvas.Handle, TRANSPARENT);
DrawText(FDrawBuffer.Canvas.Handle, PChar(Caption), Length(Caption), TextBounds, DT_SINGLELINE or DT_CENTER or DT_VCENTER);
end;
end;
procedure TProgressButton.SetProgressMin(Value: Integer);
begin
if FProgressMin <> Value then
begin
if Value > FProgressMax then
Exit;
FProgressMin := Value;
if FProgressValue < Value then
FProgressValue := Value;
PrepareDrawBuffers([btProgress]);
Invalidate;
end;
end;
procedure TProgressButton.SetProgressMax(Value: Integer);
begin
if FProgressMax <> Value then
begin
if Value < FProgressMin then
Exit;
FProgressMax := Value;
if FProgressValue > Value then
FProgressValue := Value;
PrepareDrawBuffers([btProgress]);
Invalidate;
end;
end;
procedure TProgressButton.SetProgressValue(Value: Integer);
begin
if Value < FProgressMin then
Value := FProgressMin
else
if Value > FProgressMax then
Value := FProgressMax;
if FProgressValue <> Value then
begin
FProgressValue := Value;
PrepareDrawBuffers([btProgress]);
Invalidate;
end;
end;
procedure TProgressButton.SetProgressAlpha(Value: Integer);
begin
if Value < 0 then
Value := 0
else
if Value > 175 then
Value := 175;
if FProgressAlpha <> Value then
begin
FProgressAlpha := Value;
PrepareDrawBuffers([btProgress]);
Invalidate;
end;
end;
procedure TProgressButton.SetProgressColor(Value: TColor);
begin
if Value <> FProgressColor then
begin
FProgressColor := Value;
PrepareDrawBuffers([btProgress]);
Invalidate;
end;
end;
procedure TProgressButton.SetProgressColored(Value: Boolean);
begin
if Value <> FProgressColored then
begin
FProgressColored := Value;
PrepareDrawBuffers([btProgress]);
Invalidate;
end;
end;
procedure TProgressButton.SetProgressMargins(Value: Integer);
begin
if Value <> FProgressMargins then
begin
if (Width - (2 * Value) <= 0) or (Height - (2 * Value) <= 0) or (Value < 0) then
Exit;
FProgressMargins := Value;
PrepareDrawBuffers([btProgress]);
Invalidate;
end;
end;
function TProgressButton.GetButtonState(const ItemState: UINT): TButtonState;
begin
if not Enabled then
Result := bsDisabled
else
begin
if (ItemState and ODS_SELECTED <> 0) then
Result := bsPressed
else
if FMouseInControl then
Result := bsButtonHot
else
if FFocusInControl or (ItemState and ODS_FOCUS <> 0) then
Result := bsDefault
else
Result := bsNormal;
end;
end;
procedure TProgressButton.CNDrawItem(var Msg: TWMDrawItem);
var
ButtonState: TButtonState;
begin
if not Assigned(Parent) then
Exit;
ButtonState := GetButtonState(Msg.DrawItemStruct^.itemState);
if FButtonState <> ButtonState then
begin
FButtonState := ButtonState;
PrepareDrawBuffers([btButton]);
end;
BitBlt(Msg.DrawItemStruct^.hDC, 0, 0, Width, Height, FDrawBuffer.Canvas.Handle, 0, 0, SRCCOPY);
end;
procedure TProgressButton.CMMouseEnter(var Msg: TMessage);
begin
inherited;
if not (csDesigning in ComponentState) then
begin
FMouseInControl := True;
Repaint;
end;
end;
procedure TProgressButton.CMMouseLeave(var Msg: TMessage);
begin
inherited;
if not (csDesigning in ComponentState) then
begin
FMouseInControl := False;
Repaint;
end;
end;
procedure TProgressButton.CMFontChanged(var Msg: TMessage);
begin
inherited;
PrepareDrawBuffers([btCaption]);
Invalidate;
end;
procedure TProgressButton.CMTextChanged(var Msg: TMessage);
begin
inherited;
PrepareDrawBuffers([btCaption]);
Invalidate;
end;
procedure TProgressButton.WMLButtonDblClk(var Msg: TWMLButtonDblClk);
begin
Perform(WM_LBUTTONDOWN, Msg.Keys, Longint(Msg.Pos));
end;
procedure TProgressButton.WMWindowPosChanged(var Msg: TWMWindowPosChanged);
begin
inherited;
PrepareDrawBuffers([btButton, btProgress]);
Invalidate;
end;
procedure TProgressButton.Loaded;
begin
inherited;
PrepareDrawBuffers([btButton, btProgress]);
end;
procedure TProgressButton.SetButtonStyle(Value: Boolean);
begin
if Value <> FFocusInControl then
begin
FFocusInControl := Value;
Invalidate;
end;
end;
procedure TProgressButton.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or BS_OWNERDRAW;
end;
procedure Register;
begin
RegisterComponents('StackOverflow', [TProgressButton]);
end;
end.
这是
最新版本
。我现在没有时间来描述它和完成演示。它终于继承自
TCustomButton
,支持动作图像(有一个新属性
ImageSource
,指定将用作图像源的内容,
isNone
=无图像;
isAction
=从操作的图像列表中获取图像;
isCustom
=使用
Images
列表)。
待续 :)
以下是其可能的外观: