关于创建"容器"的建议部件

时间:2015-10-13 13:25:17

标签: delphi delphi-xe2

我想创建一个"面板组件"通过y按钮按住x(实际上不是按钮,但你明白了)。

它应该看起来像扫雷,你可以点击任何按钮,每个按钮都有相同的"全局点击"事件。但是使用发件人和按钮作为参数,例如:Sender: TObject; Button: TButton)作为面板组件的wherin Sender和按钮面板内的按钮。

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

  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. 我确信我需要覆盖resize方法。我有一些想法,但我没有那么远。

1 个答案:

答案 0 :(得分:0)

这样的东西?

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.

我确信上面的代码提供了大量的学习资料,思考的食物和定制的空间。简而言之:

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

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

玩得开心。