如何制作带有圆角的TFrame?

11
我希望基于TFrame制作一个组件,该组件包含TLMDShapeControl(用于绘制圆角背景)和一个TEdit控件(也可以是TComboBoxTDBEdit等)。之后,我将使用“添加到工具箱”命令将其转换为可重复使用的组件控件。
问题在于,我需要它具有宽度灵活性,为此我想到了将Frame中的所有内容设置为alClient并对TEdit进行5像素边距,以便用户可以看到圆角。但这样做太糟糕了,因为我不能使用Align来设置组件的位置,只能每次都将它们复制粘贴。唯一正确的方法是只使用带有alClient和5px边距的TEdit,而不使用TShape。相反,我可以使TFrame具有圆角和透明度,这样它就不会在不同颜色或TImages下显得丑陋。
但我该如何做呢?有人有代码示例吗? 目标:透明圆角

2
如果我想让这个正常工作,我会创建自己的控件并将其安装为一个包,其中包含外部和内部控件,并包含所有代码以使其正常工作。TFrame不是正确的父类,我会使用普通的TCustomControl。框架是用于在设计时组合可视控件而不是在编译时。但是编译自己的自定义控件是更可靠和弹性的解决方案。 - Warren P
1
我已经这样做了,使用内置的 TShape 来替代 TLMDShapeControl,它可以正常运行。但最终,我放弃了这些样式,因为我的客户讨厌这些非本地的样式,并希望它们消失。 - Warren P
是的,TShape 可以有圆角,但输出结果非常不稳定。我没有团队,所以我是那个调查需求、制作项目、编写代码和设计系统及数据库的人,但我还没有成为其中任何一项的专家... 还! :-) - NaN
使用 CreateRoundRectRgn 也是同样的情况。GDI不支持任何类型的抗锯齿,因此您无法获得平滑的圆角。如果我是您,我会像Warren建议的那样使用一个TCustomControl,例如这样。然后就只有两个问题,一个是此类控件的透明度,另一个是需要一个能够呈现平滑弧线的库来处理您的圆角。 - TLama
这就是我现在的做法。但这样并不太好,对吧? - NaN
显示剩余2条评论
1个回答

16
回答你的问题,如何制作带有圆角的框架,你可以尝试像这样的方法,但是由于此处使用的CreateRoundRectRgn没有反锯齿效果,所以你可能会对结果不满意。
type
  TFrame1 = class(TFrame)
    Edit1: TEdit;
    Button1: TButton;
  protected
    procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
  end;

implementation

procedure TFrame1.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
var
  Region: HRGN;
begin
  inherited;
  Region := CreateRoundRectRgn(0, 0, ClientWidth, ClientHeight, 30, 30);
  SetWindowRgn(Handle, Region, True);
end;

更新:

由于GDI没有支持绘制带有抗锯齿的圆弧形状的功能,因此我在这里发布了一个纯填充的圆角矩形示例(仅使用GDI+实现,需要从这里获取GDI+包装器)。

以下属性对其使用很重要:

  • Color - 是形状的填充颜色(可以增强画笔颜色、梯度等)
  • Radius - 是用于绘制圆角的圆的半径(以像素为单位)
  • AlphaValue - 是呈现的圆角矩形的不透明度值(只是为了好玩^_^)

unit RoundShape;

interface

uses
  SysUtils, Classes, Controls, Graphics, GdiPlus;

type
  TCustomRoundShape = class(TGraphicControl)
  private
    FRadius: Integer;
    FAlphaValue: Integer;
    procedure SetRadius(Value: Integer);
    procedure SetAlphaValue(Value: Integer);
  protected
    procedure Paint; override;
    property Radius: Integer read FRadius write SetRadius default 10;
    property AlphaValue: Integer read FAlphaValue write SetAlphaValue default 255;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TRoundShape = class(TCustomRoundShape)
  public
    property Canvas;
  published
    property Align;
    property AlphaValue;
    property Anchors;
    property Color;
    property Constraints;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property Radius;
    property ShowHint;
    property Visible;
    property OnClick;
    property OnContextPopup;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnMouseActivate;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDock;
    property OnStartDrag;
  end;

procedure Register;

implementation

{ TCustomRoundShape }

constructor TCustomRoundShape.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Width := 213;
  Height := 104;
  FRadius := 10;
  FAlphaValue := 255;
end;

procedure TCustomRoundShape.SetRadius(Value: Integer);
begin
  if FRadius <> Value then
  begin
    FRadius := Value;
    Invalidate;
  end;
end;

procedure TCustomRoundShape.SetAlphaValue(Value: Integer);
begin
  if FAlphaValue <> Value then
  begin
    FAlphaValue := Value;
    Invalidate;
  end;
end;

procedure TCustomRoundShape.Paint;
var
  GPPen: TGPPen;
  GPColor: TGPColor;
  GPGraphics: IGPGraphics;
  GPSolidBrush: IGPSolidBrush;
  GPGraphicsPath: IGPGraphicsPath;
begin
  GPGraphicsPath := TGPGraphicsPath.Create;
  GPGraphicsPath.Reset;
  GPGraphicsPath.AddArc(0, 0, FRadius, FRadius, 180, 90);
  GPGraphicsPath.AddArc(ClientWidth - FRadius - 1, 0, FRadius, FRadius, 270, 90);
  GPGraphicsPath.AddArc(ClientWidth - FRadius - 1, ClientHeight - FRadius - 1,
    FRadius, FRadius, 0, 90);
  GPGraphicsPath.AddArc(0, ClientHeight - FRadius - 1, FRadius, FRadius, 90, 90);
  GPGraphicsPath.CloseFigure;

  GPColor.InitializeFromColorRef(ColorToRGB(Color));
  GPColor.Alpha := FAlphaValue;
  GPPen := TGPPen.Create(GPColor);
  GPSolidBrush := TGPSolidBrush.Create(GPColor);

  GPGraphics := TGPGraphics.Create(Canvas.Handle);
  GPGraphics.SmoothingMode := SmoothingModeAntiAlias;
  GPGraphics.FillPath(GPSolidBrush, GPGraphicsPath);
  GPGraphics.DrawPath(GPPen, GPGraphicsPath);
end;

procedure Register;
begin
  RegisterComponents('Stack Overflow', [TRoundShape]);
end;

end.

下面是使用 SmoothingModeAntiAlias 平滑模式的结果:

图片描述

虽然使用 GDI+ 来处理这样微小的事情会带来一些额外负担,但如果不启用反锯齿,使用纯 GDI 渲染得到的效果则比较丑陋。以下是同一个圆角矩形使用纯 GDI 渲染的示例:

图片描述


那回答了我的问题。我会尝试制作一个组件,让我们可以选择在圆形上方想要的控件类型,并查看它是否比tframe方式更好。谢谢! - NaN
不客气!但是我必须警告你;如果你像我在上面的评论中提到的那样遵循TCustomControl的方式,要小心透明度。例如,当Windows主题被禁用时,我一直在尝试使容器控件(例如TPanel)透明,但从未令人满意。 - TLama
我们如何将这个新组件应用于圆角框架中? - NaN
这只是一个带有抗锯齿边角的形状组件,可以用它来替换TShape。我在这里发布了它,因为你在上面的评论中讨论了TShape。但是,框架可以是透明的(ParentBackground设置为True),因此将此形状放置在您的控件下方可能已经足够了。 - TLama
2
如果您选择使用“区域”方法,请不要忘记在每次调整窗口大小时调整该区域的大小。仅在CreateWnd()中创建固定大小的区域是不够的。 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接