Компонент кнопки, влияющий на родительскую панель в Delphi
Вопрос
Я пишу программу, состоящую из динамически создаваемых панелей, в каждой из которых есть несколько компонентов, в том числе кнопки удаления и добавления панели. Каждая панель отображает число пикселей, в 20 раз превышающее номер панели, друг над другом, OnClick для добавления должен добавить еще одну панель в конец набора, а OnClick для удаления должен уничтожить его родительский элемент, а затем переместить все остальные панели вверх в пространство, которое удаляется. Метод, который я уже пробовал, включал использование массива, но, к сожалению, я получил EAccessViolation при циклическом просмотре массива, в котором я удалил объект в середине.
Извините, если это очевидно, или на него уже был дан ответ, но я только начал учить себя ОО в начале этой недели, поэтому я не знаю всех терминологий или есть класс, похожий на массив, который сделает эти вещи для меня.
Решение
Возможно, вам лучше сделать это, осторожно используя свойство Align.
Если у меня есть три панели с выравниванием, как указано здесь:
|-----------------------|
| |
| alTop |
| |
|-----------------------|
|-----------------------|
| |
| alTop |
| |
|-----------------------|
|-----------------------|
| |
| alTop |
| |
|-----------------------|
И я удаляю второй, затем третий автоматически встанет на свое место.
Просто поместите все три панели внутри другого родительского элемента управления (то есть другой панели), чтобы определить, что " top " означает, когда мы говорим «alTop».
Если вы хотите анимировать эффект, вам придется немного поумнеть. Это твоя цель? Если так, я уверен, что мы можем что-то придумать.
Правка - я написал код, который может дать вам несколько идей:
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls;
type
TWhere = (wAtBeginning, wAtEnd);
type
TfrmMain = class(TForm)
panCtrl: TPanel;
panHost: TPanel;
btnAddPan: TBitBtn;
btnDelPan: TBitBtn;
lbAddWhere: TListBox;
lbDelWhere: TListBox;
procedure btnAddPanClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure btnDelPanClick(Sender: TObject);
private
function GetPanel(HostPanel: TPanel; Where: TWhere): TPanel;
function BottomOfLastPanel(HostPanel: TPanel): integer;
procedure AddPanel(HostPanel: TPanel; AddWhere: TWhere);
procedure DelPanel(HostPanel: TPanel; DelWhere: TWhere);
procedure DelThisPanel(Sender: TObject);
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.AddPanel(HostPanel: TPanel; AddWhere: TWhere);
var
pnl: TPanel;
btn: TBitBtn;
begin
pnl := TPanel.Create(HostPanel);
with pnl do begin
case AddWhere of
wAtBeginning: Top := 0;
wAtEnd: Top := BottomOfLastPanel(HostPanel);
end;
Align := alTop;
Parent := HostPanel;
Caption := DateTimeToStr(Now);
end;
btn := TBitBtn.Create(pnl);
with btn do begin
Parent := pnl;
Left := 0;
Top := 0;
Width := 100;
Height := 30;
Align := alLeft;
Caption := 'Delete this panel';
OnClick := DelThisPanel;
end;
end;
function TfrmMain.BottomOfLastPanel(HostPanel: TPanel): integer;
begin
//scan through all panels contained inside the host panel
//return the bottom of the lowest one (highest "top" value)
Result := 0;
if Assigned(GetPanel(HostPanel,wAtEnd)) then begin
Result := GetPanel(HostPanel,wAtEnd).Top + GetPanel(HostPanel,wAtEnd).Height;
end;
end;
procedure TfrmMain.btnAddPanClick(Sender: TObject);
begin
case lbAddWhere.ItemIndex of
0: AddPanel(panHost,wAtBeginning);
1: AddPanel(panHost,wAtEnd);
end;
end;
procedure TfrmMain.btnDelPanClick(Sender: TObject);
begin
case lbDelWhere.ItemIndex of
0: DelPanel(panHost,wAtBeginning);
1: DelPanel(panHost,wAtEnd);
end;
end;
procedure TfrmMain.DelPanel(HostPanel: TPanel; DelWhere: TWhere);
var
pnlToDelete: TPanel;
begin
case DelWhere of
wAtBeginning: pnlToDelete := GetPanel(HostPanel,wAtBeginning);
wAtEnd: pnlToDelete := GetPanel(HostPanel,wAtEnd);
end;
if Assigned(pnlToDelete) then begin
FreeAndNil(pnlToDelete);
end;
end;
procedure TfrmMain.DelThisPanel(Sender: TObject);
var
parentPnl: TPanel;
begin
//delete the parent panel of this button
if Sender is TBitBtn then begin
if (Sender as TBitBtn).Parent is TPanel then begin
parentPnl := (Sender as TBitBtn).Parent as TPanel;
parentPnl.Parent := nil;
FreeAndNil(parentPnl);
end;
end;
end;
procedure TfrmMain.FormShow(Sender: TObject);
begin
lbAddWhere.ItemIndex := 1;
lbDelWhere.ItemIndex := 1;
end;
function TfrmMain.GetPanel(HostPanel: TPanel; Where: TWhere): TPanel;
var
i: integer;
begin
Result := nil;
for i := 0 to panHost.ControlCount - 1 do begin
if panHost.Controls[i] is TPanel then begin
Result := (panHost.Controls[i] as TPanel);
if Where = wAtBeginning then begin
Break;
end;
end;
end;
end;
end.
А вот код для DFM:
object frmMain: TfrmMain
Left = 0
Top = 0
Caption = 'Add / Delete Panel Demo'
ClientHeight = 520
ClientWidth = 637
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object panCtrl: TPanel
Left = 0
Top = 0
Width = 305
Height = 520
Align = alLeft
TabOrder = 0
object btnAddPan: TBitBtn
Left = 8
Top = 8
Width = 125
Height = 75
Caption = 'Add panel'
TabOrder = 0
OnClick = btnAddPanClick
end
object btnDelPan: TBitBtn
Left = 8
Top = 89
Width = 125
Height = 75
Caption = 'Remove panel'
TabOrder = 1
OnClick = btnDelPanClick
end
object lbAddWhere: TListBox
Left = 139
Top = 8
Width = 150
Height = 75
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Tahoma'
Font.Style = []
ItemHeight = 16
Items.Strings = (
'Add to the top'
'Add to the bottom')
ParentFont = False
TabOrder = 2
end
object lbDelWhere: TListBox
Left = 139
Top = 89
Width = 150
Height = 75
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Tahoma'
Font.Style = []
ItemHeight = 16
Items.Strings = (
'Delete from the top'
'Delete from the bottom')
ParentFont = False
TabOrder = 3
end
end
object panHost: TPanel
Left = 305
Top = 0
Width = 332
Height = 520
Align = alClient
TabOrder = 1
ExplicitLeft = 392
ExplicitTop = 264
ExplicitWidth = 185
ExplicitHeight = 41
end
end
Другие советы
Вы можете использовать свою стратегию массива, если вы используете динамический массив и фактически удаляете элементы при удалении панелей. Кроме того, вы всегда можете проверить, назначен ли элемент, если присвоено (массив [I]).
Тем не менее, было бы гораздо лучше заменить решение массива решением, использующим TComponentList, которое упростит добавление и удаление панелей в списке и предназначено именно для такого типа ситуаций.