delphi - 关于创建 "container"组件的建议

标签 delphi delphi-xe2

我想创建一个“面板组件”来容纳 x by y 按钮(实际上不完全是按钮,但你明白了)。

它应该看起来像一个扫雷游戏,您可以单击任何按钮,并且每个按钮都有相同的“全局单击”事件。但使用发送者和按钮作为参数,例如: Sender: TObject; Button: TButton),其中Sender是面板组件,Button是面板内的按钮。

到目前为止,我设置了两个属性:水平方向的按钮数量和垂直方向的按钮数量。

  property ButtonsHeight: Integer read fButtonsHeight write SetButtonsHeight;
  property ButtonsWidth: Integer read fButtonsWidth  write SetButtonsWidth;

procedure TMultipleDrawPanel.SetButtonsHeight(const Value: Integer);
begin
  if Value < 1 then begin
    raise Exception.Create('Mumarul minim de butoane este 1!');
  end;
  InflateButtonsHeight(fButtonsHeight, Value);
  fButtonsHeight := Value;
end;

这是改变一个方向按钮数量的伪代码:

procedure TMultipleDrawPanel.InflateButtonsHeight(oldValue, newValue: Integer);
begin
  if oldValue < newValue then begin
    // free extra buttons
  end else begin
    // create new buttons
  end;
end;

有人能给我一些建议吗?

  1. 如何存储按钮列表?
  2. 当数字从较高值变为较低值时如何释放按钮? (否则如何创建新的?)
  3. 我确定我需要覆盖调整大小方法。我有一些想法,但还没有实现。

最佳答案

类似这样的吗?

unit ButtonPanel;

interface

uses
  System.Classes, System.SysUtils, Vcl.Controls, Vcl.StdCtrls, System.Math;

type
  TCoord = record
    Col: Integer;
    Row: Integer;
  end;

  TCustomButtonPanel = class;

  TButtonClickEvent = procedure(Sender: TCustomButtonPanel;
    Coord: TCoord) of object;

  TCustomButtonPanel = class(Vcl.Controls.TWinControl)
  private
    FColCount: Integer;
    FOnClick: TButtonClickEvent;
    FRowCount: Integer;
    procedure ButtonClick(Sender: TObject);
    function CoordFromIndex(Index: Integer): TCoord;
    function CoordToIndex(Col, Row: Integer): Integer;
    function GetButton(Col, Row: Integer): TButton; overload;
    function GetButton(Index: Integer): TButton; overload;
    function GetButtonCount: Integer;
    procedure SetColCount(Value: Integer);
    procedure SetRowCount(Value: Integer);
    procedure SizeChanged;
  protected
    function CanResize(var NewWidth, NewHeight: Integer): Boolean; override;
    procedure DoClick(Index: Integer); virtual;
    procedure Resize; override;
    procedure ValidateInsert(AComponent: TComponent); override;
    property ButtonCount: Integer read GetButtonCount;
    property Buttons[Col, Row: Integer]: TButton read GetButton;
    property ColCount: Integer read FColCount write SetColCount default 5;
    property OnClick: TButtonClickEvent read FOnClick write FOnClick;
    property RowCount: Integer read FRowCount write SetRowCount default 5;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TButtonPanel = class(TCustomButtonPanel)
  public
    property ButtonCount;
    property Buttons;
  published
    property ColCount;
    property OnClick;
    property RowCount;
  end;

implementation

type
  TButtonPanelButton = class(Vcl.StdCtrls.TButton);

resourcestring
  SInvalidControlType = 'Invalid control type for ButtonPanel child';

function Round(Value, Rounder: Integer): Integer; overload;
begin
  if Rounder = 0 then
    Result := Value
  else
    Result := (Value div Rounder) * Rounder;
end;

{ TCustomButtonPanel }

procedure TCustomButtonPanel.ButtonClick(Sender: TObject);
begin
  DoClick(TButton(Sender).Tag);
end;

function TCustomButtonPanel.CanResize(var NewWidth, NewHeight: Integer): Boolean;
var
  EdgeSize: Integer;
begin
  Result := inherited CanResize(NewWidth, NewHeight);
  EdgeSize := 2 * (BorderWidth + BevelWidth);
  NewWidth := Round(NewWidth - EdgeSize, FColCount) + EdgeSize;
  NewHeight := Round(NewHeight - EdgeSize, FRowCount) + EdgeSize;
end;

function TCustomButtonPanel.CoordFromIndex(Index: Integer): TCoord;
begin
  Result.Col := Index mod ColCount;
  Result.Row := Index div ColCount;
end;

function TCustomButtonPanel.CoordToIndex(Col, Row: Integer): Integer;
begin
  Result := FColCount * Row + Col;
end;

constructor TCustomButtonPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := [];
  FColCount := 5;
  FRowCount := 5;
  SizeChanged;
end;

procedure TCustomButtonPanel.DoClick(Index: Integer);
begin
  if Assigned(FOnClick) then
    FOnClick(Self, CoordFromIndex(Index));
end;

function TCustomButtonPanel.GetButton(Col, Row: Integer): TButton;
begin
  Result := GetButton(CoordToIndex(Col, Row));
end;

function TCustomButtonPanel.GetButton(Index: Integer): TButton;
begin
  Result := TButton(Controls[Index]);
end;

function TCustomButtonPanel.GetButtonCount: Integer;
begin
  Result := ControlCount;
end;

procedure TCustomButtonPanel.Resize;
var
  ColWidth: Integer;
  RowHeight: Integer;
  Col: Integer;
  Row: Integer;
begin
  inherited Resize;
  ColWidth := ClientWidth div FColCount;
  RowHeight := ClientHeight div FRowCount;
  for Col := 0 to FColCount - 1 do
    for Row := 0 to FRowCount - 1 do
      Buttons[Col, Row].SetBounds(Col * ColWidth, Row * RowHeight, ColWidth,
        RowHeight);
end;

procedure TCustomButtonPanel.SetColCount(Value: Integer);
begin
  if FColCount <> Value then
  begin
    FColCount := Max(1, Value);
    SizeChanged;
  end;
end;

procedure TCustomButtonPanel.SetRowCount(Value: Integer);
begin
  if FRowCount <> Value then
  begin
    FRowCount := Max(1, Value);
    SizeChanged;
  end;
end;

procedure TCustomButtonPanel.SizeChanged;
var
  I: Integer;
  OldCount: Integer;
  NewCount: Integer;
  Button: TButton;
begin
  OldCount := ButtonCount;
  NewCount := FColCount * FRowCount;
  for I := OldCount - 1 downto NewCount do
    GetButton(I).Free;
  for I := OldCount to NewCount - 1 do
  begin
    Button := TButtonPanelButton.Create(Self);
    Button.Tag := I;
    Button.OnClick := ButtonClick;
    Button.Parent := Self;
  end;
  AdjustSize;
end;

procedure TCustomButtonPanel.ValidateInsert(AComponent: TComponent);
begin
  inherited ValidateInsert(AComponent);
  if not (AComponent is TButtonPanelButton) then
    raise EInvalidInsert.Create(SInvalidControlType);
end;

end.

我确信上面的代码提供了大量的学习 Material 、深思熟虑和定制空间。简而言之:

  • 滥用组件的Controls属性来将其含义扩展到按钮。因此,该控件不允许插入其他类型的控件。请参阅GetButtonGetButtonCountValidateInsert
  • 由于每个按钮具有相同的大小,并且组件完全充满按钮,因此组件的大小限制为按钮大小的倍数。请参阅CanResize
  • 它重新引入了 OnClick 事件,因为面板本身不会被单击(它充满了按钮)。该事件有一个额外的参数,请参阅 ButtonClickDoClick
  • 二维列行坐标映射到索引线性数组,请参阅 CoordFromIndexCoordToIndex
  • 按钮由 Tag 属性标识,请参阅创建按钮的 SizeChanged

您的下一步是使用虚拟方法重新设计它,其中不使用实际的 TButton 控件,而只是模仿绘制按钮。

玩得开心。

关于delphi - 关于创建 "container"组件的建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33103900/

相关文章:

Delphi FireMonkey 存储来自组件的信息

delphi - 如何在TVirtualStringTree中绘制动画水平条?

macos - 如何在 Delphi XE2 中向 Mac OS Finder 添加菜单项

delphi - TCP 客户端未接收到来自 RTSP 服务器的响应

delphi - 从 Windows 任务栏中删除 firemonkey

sqlite - Delphi SqLite 日期加载到 TDateEdit 错误

delphi - 设置自定义控件的默认属性

delphi - Embarcadero Rad Studio 中的 TPanel

delphi - 从指向记录类型 rtti 字段的指针获取值

delphi - Delphi 中嵌套枚举的自己的命名空间