我想创建一个“面板组件”来容纳 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;
有人能给我一些建议吗?
- 如何存储按钮列表?
- 当数字从较高值变为较低值时如何释放按钮? (否则如何创建新的?)
- 我确定我需要覆盖调整大小方法。我有一些想法,但还没有实现。
最佳答案
类似这样的吗?
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
属性来将其含义扩展到按钮。因此,该控件不允许插入其他类型的控件。请参阅GetButton
、GetButtonCount
、ValidateInsert
。 - 由于每个按钮具有相同的大小,并且组件完全充满按钮,因此组件的大小限制为按钮大小的倍数。请参阅
CanResize
。 - 它重新引入了
OnClick
事件,因为面板本身不会被单击(它充满了按钮)。该事件有一个额外的参数,请参阅ButtonClick
、DoClick
。 - 二维列行坐标映射到索引线性数组,请参阅
CoordFromIndex
、CoordToIndex
。 - 按钮由
Tag
属性标识,请参阅创建按钮的SizeChanged
。
您的下一步是使用虚拟方法重新设计它,其中不使用实际的 TButton
控件,而只是模仿绘制按钮。
玩得开心。
关于delphi - 关于创建 "container"组件的建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33103900/