关于创建 "container" 组件的建议
Advice on creating a "container" component
我想创建一个 "panel component" 来容纳 x x y 按钮(实际上不完全是按钮,但你明白了)。
它应该看起来像一个扫雷器,您可以在其中单击任何按钮并且每个按钮都有相同的 "global click" 事件。但是使用 sender 和 button 作为参数,例如:Sender: TObject; Button: TButton
) wherein 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
控件,而按钮只是模仿绘制的。
玩得开心。
我想创建一个 "panel component" 来容纳 x x y 按钮(实际上不完全是按钮,但你明白了)。
它应该看起来像一个扫雷器,您可以在其中单击任何按钮并且每个按钮都有相同的 "global click" 事件。但是使用 sender 和 button 作为参数,例如:Sender: TObject; Button: TButton
) wherein 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
控件,而按钮只是模仿绘制的。
玩得开心。