Wojciech \"Spook\" Sura Guest
|
Posted: Thu Feb 15, 2007 3:34 am Post subject: [Long] TCollectionItem - reaction for properties changing |
|
|
Hi!
I'm writing enhanced grid component, which consists of some other components
(TStringGrid, THeaderControl and TEdits).
The class ownership is:
TSpkGridColumn = class(TCollectionItem)
|
TSpkGridColumns = class(TCollection)
|
TSpkGrid = class(TPanel)
I want the TSpkGrid component to be informed in designtime about any changes
in TSpkGridColumn's - to properly change all internal components.
I've tried:
1. Writing proper events passed from TSpkGridColumn.SetProperty thru
TSpkGridColumns.SpkGridColumnEventHandler to TSpkGrid.EventHandler. That is,
when the property was changed in TSpkGridColumn, it executed the event which
was "cought" by TSpkGridColumns, which then executed event "cought" by
TSpkGrid.
It worked perfectly... in runtime. When I added elements (columns) in IDE,
the events weren't properly assigned though assigned in TSpkGridColumns.add
and TSpkGridColumns.assign.
2. Forcing execution of owner's method, the "back-inheritance", that is:
procedure TSpkGridColumn.SetProperty(AProperty : integer);
begin
FProperty:=AProperty;
(Collection as TSpkGridColumns).EventHandler;
end;
However, I've recieved numerous errors saying "Error reading
TSpkGridColumn.Property: List index out of bounds: 0"
It's very strange, because always before iterating thru Collection items, I
do a check:
if FColumns.count>0 then
for i:=0 to FColumns.count-1 do...
Can someone help?...
I include the source of component below:
<Code>
unit SpkGrid;
interface
uses Classes, StdCtrls, ExtCtrls, ComCtrls, Controls, Grids, SysUtils, Math,
Contnrs, Forms;
type TSpkGrid = class;
TSpkGridColumn = class;
TSpkGridColumns = class;
TSpkGridColumn = class(TCollectionItem)
private
FWidth : integer;
FCaption : string;
protected
procedure SetWidth(AWidth : integer);
procedure SetCaption(ACaption : string);
public
constructor create(Collection: TSpkGridColumns); reintroduce;
destructor destroy; override;
published
property Width : integer read FWidth write SetWidth;
property Caption : string read FCaption write SetCaption;
end;
TSpkGridColumns = class(TCollection)
private
FOwnerGrid : TSpkGrid;
FOnColumnsChanged : TNotifyEvent;
FOnVisualsChanged : TNotifyEvent;
protected
function GetItem(index : integer) : TSpkGridColumn; reintroduce;
procedure SetItem(index : integer; AItem : TSpkGridColumn);
reintroduce;
procedure ColumnVisualsChangedHandler(Sender : TObject);
procedure ColumnChangedHandler(Sender : TObject);
public
function GetOwner : TPersistent; override;
constructor create(AOwnerGrid : TSpkGrid); reintroduce;
destructor destroy; override;
function Add : TSpkGridColumn; reintroduce;
procedure Delete(index : integer);
procedure Assign(Target : TPersistent); override;
procedure Update(item : TCollectionItem); override;
function Insert(Index: Integer): TSpkGridColumn;
property Items[index : integer] : TSpkGridColumn read GetItem write
SetItem;
published
property OnColumnsChanged : TNotifyEvent read FOnColumnsChanged write
FOnColumnsChanged;
property OnVisualsChanged : TNotifyEvent read FOnVisualsChanged write
FOnVisualsChanged;
end;
TEditArray = array of TEdit;
TSpkGrid = class(TPanel)
private
FColumns : TSpkGridColumns;
FOnColumnVisualsChanged : TNotifyEvent;
FOnColumnsChanged : TNotifyEvent;
FGrid : TStringGrid;
FHeaders : THeaderControl;
FEditPanel : TPanel;
FEditList : TEditArray;
protected
procedure ColumnsChangedHandler(Sender : TObject);
procedure ColumnVisualsChangedHandler(Sender : TObject);
procedure TopLeftChangedHandler(Sender : TObject);
procedure SectionResizeHandler(HeaderControl: THeaderControl;
Section: THeaderSection);
procedure SetColumns(Value : TSpkGridColumns);
public
constructor create(AOwner : TComponent); override;
destructor destroy; override;
procedure RebuildColumns;
published
property OnColumnVisualsChanged : TNotifyEvent read
FOnColumnVisualsChanged write FOnColumnVisualsChanged;
property OnColumnsChanged : TNotifyEvent read FOnColumnsChanged write
FOnColumnsChanged;
property Columns : TSpkGridColumns read FColumns write SetColumns;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('SpookSoft',[TSpkGrid]);
end;
{ TSpkGridColumn }
procedure TSpkGridColumn.SetWidth(AWidth : integer);
begin
FWidth:=AWidth;
end;
procedure TSpkGridColumn.SetCaption(ACaption : string);
begin
FCaption:=ACaption;
end;
constructor TSpkGridColumn.create(Collection: TSpkGridColumns);
begin
inherited create(Collection);
FWidth:=150;
FCaption:='Kolumna';
end;
destructor TSpkGridColumn.destroy;
begin
inherited destroy;
end;
{ TSpkGridColumns }
procedure TSpkGridColumns.SetItem(index : integer; AItem : TSpkGridColumn);
begin
inherited SetItem(index,AItem);
end;
function TSpkGridColumns.GetItem(index : integer) : TSpkGridColumn;
begin
result:=inherited GetItem(index) as TSpkGridColumn;
end;
procedure TSpkGridColumns.ColumnVisualsChangedHandler(Sender : TObject);
begin
if assigned(FOnVisualsChanged) then
FOnVisualsChanged(self);
end;
procedure TSpkGridColumns.ColumnChangedHandler(Sender : TObject);
begin
if assigned(FOnColumnsChanged) then
FOnColumnsChanged(self);
end;
function TSpkGridColumns.GetOwner : TPersistent;
begin
result:=FOwnerGrid;
end;
constructor TSpkGridColumns.create(AOwnerGrid : TSpkGrid);
begin
inherited create(TSpkGridColumn);
FOwnerGrid:=AOwnerGrid;
FOnColumnsChanged:=nil;
FOnVisualsChanged:=nil;
end;
destructor TSpkGridColumns.destroy;
begin
inherited destroy;
end;
function TSpkGridColumns.Add : TSpkGridColumn;
begin
result:=TSpkGridColumn(inherited Add);
if assigned(FOnColumnsChanged) then
FOnColumnsChanged(self);
end;
procedure TSpkGridColumns.Delete(index : integer);
begin
inherited Delete(index);
if assigned(FOnColumnsChanged) then
FOnColumnsChanged(self);
end;
procedure TSpkGridColumns.Assign(Target : TPersistent);
begin
inherited Assign(Target);
if assigned(FOnColumnsChanged) then
FOnColumnsChanged(self);
end;
procedure TSpkGridColumns.Update(item : TCollectionItem);
begin
inherited Update(item);
if assigned(FOnColumnsChanged) then
FOnColumnsChanged(self);
end;
function TSpkGridColumns.Insert(Index: Integer): TSpkGridColumn;
begin
result:=TSpkGridColumn(inherited insert(index));
if assigned(FOnColumnsChanged) then
FOnColumnsChanged(self);
end;
{ TSpkGrid }
procedure TSpkGrid.ColumnsChangedHandler(Sender : TObject);
begin
RebuildColumns;
if assigned(FOnColumnsChanged) then
FOnColumnsChanged(self);
end;
procedure TSpkGrid.ColumnVisualsChangedHandler(Sender : TObject);
var i : integer;
wd : integer;
begin
if FColumns.count>0 then
begin
// Header
for i:=0 to FColumns.count-1 do
begin
with FHeaders.Sections.Items[i] do
begin
if Width<>FColumns.items[i].Width then
Width:=FColumns.items[i].Width;
if Text<>FColumns.items[i].Caption then
Text:=FColumns.items[i].Caption;
end;
end;
// Edity
wd:=0;
for i:=0 to FColumns.count-1 do
begin
with FEditList[i] do
begin
if left<>wd then
Left:=wd;
if Width<>FColumns.items[i].Width then
Width:=FColumns.items[i].Width;
wd:=wd+FColumns.items[i].Width;
end;
end;
end;
if FColumns.count>0 then
for i:=0 to FColumns.count-1 do
FGrid.ColWidths[i]:=FColumns.Items[i].Width-1;
FGrid.Repaint;
if assigned(FOnColumnVisualsChanged) then
FOnColumnVisualsChanged(self);
end;
procedure TSpkGrid.TopLeftChangedHandler(Sender : TObject);
begin
if (Sender as TStringGrid).LeftCol<>0 then
(Sender as TStringGrid).LeftCol:=0;
end;
procedure TSpkGrid.SectionResizeHandler(HeaderControl: THeaderControl;
Section: THeaderSection);
var i,wd : integer;
begin
FColumns.items[Section.Index].Width:=Section.width;
// Rewymiarowanie komponentów
if FColumns.count>0 then
begin
// Header
for i:=0 to FColumns.count-1 do
begin
with FHeaders.Sections.Items[i] do
begin
if Width<>FColumns.items[i].Width then
Width:=FColumns.items[i].Width;
if Text<>FColumns.items[i].Caption then
Text:=FColumns.items[i].Caption;
end;
end;
// Edity
wd:=0;
for i:=0 to FColumns.count-1 do
begin
with FEditList[i] do
begin
if left<>wd then
Left:=wd;
if Width<>FColumns.items[i].Width then
Width:=FColumns.items[i].Width;
wd:=wd+FColumns.items[i].Width;
end;
end;
end;
if FColumns.count>0 then
for i:=0 to FColumns.count-1 do
FGrid.ColWidths[i]:=FColumns.Items[i].Width-1;
FGrid.Repaint;
if assigned(FOnColumnVisualsChanged) then
FOnColumnVisualsChanged(self);
end;
procedure TSpkGrid.RebuildColumns;
var i : integer;
wd : integer;
begin
// Usuwamy zbędne edity
while length(FEditList)>FColumns.Count do
begin
FEditList[high(FEditList)].free;
setlength(FEditList,length(FEditList)-1);
end;
// Dodajemy potrzebne edity
while length(FEditList)<FColumns.count do
begin
setlength(FEditList,length(FEditList)+1);
FEditList[high(FEditList)]:=TEdit.create(FEditPanel);
FEditList[high(FEditList)].parent:=FEditPanel;
end;
// Ustawiamy odpowiednie pozycje editów
wd:=0;
if length(FEditList)>0 then
for i:=0 to length(FEditList)-1 do
begin
FEditList[i].left:=wd;
FEditList[i].width:=FColumns.Items[i].width;
wd:=wd+FColumns.Items[i].width;
FEditList[i].height:=21;
end;
// Ustawiamy odpowiednie opcje Headera
FHeaders.Sections.Clear;
if FColumns.count>0 then
for i:=0 to FColumns.count-1 do
begin
with FHeaders.Sections.Add do
begin
if Width<>FColumns.items[i].Width then
Width:=FColumns.items[i].Width;
if Text<>FColumns.items[i].Caption then
Text:=FColumns.items[i].Caption;
end;
end;
// Zmieniamy ustawienia Grida
// Ustawiam na 1, bo na 0 się nie da ;]
// Tak czy inaczej, gdy FColumns.count=0, Items nie pozwolą dostać się do
// tego zerowego wiersza, więc wszystko będzie w porządku.
FGrid.ColCount:=max(1,FColumns.count);
// Rows zostawiamy w spokoju
if FColumns.count>0 then
for i:=0 to FColumns.count-1 do
FGrid.ColWidths[i]:=FColumns.Items[i].Width-1;
end;
procedure TSpkGrid.SetColumns(Value : TSpkGridColumns);
begin
FColumns.Assign(Value);
end;
constructor TSpkGrid.create(AOwner : TComponent);
begin
inherited create(AOwner);
BevelInner:=bvNone;
BevelOuter:=bvNone;
// Własności
FOnColumnsChanged:=nil;
FColumns:=TSpkGridColumns.create(self);
FColumns.OnColumnsChanged:=self.ColumnsChangedHandler;
FColumns.OnVisualsChanged:=self.ColumnVisualsChangedHandler;
// Nagłówki
FHeaders:=THeaderControl.create(self);
FHeaders.parent:=self;
FHeaders.align:=alTop;
FHeaders.OnSectionResize:=self.SectionResizeHandler;
// Panel dla editów
FEditPanel:=TPanel.create(self);
FEditPanel.parent:=self;
FEditPanel.top:=FHeaders.top+FHeaders.height+1;
FEditPanel.align:=alTop;
FEditPanel.BevelInner:=bvNone;
FEditPanel.BevelOuter:=bvNone;
FEditPanel.height:=21;
// StringGrid
FGrid:=TStringGrid.create(self);
FGrid.parent:=self;
FGrid.top:=FEditPanel.top+FEditPanel.height+1;
FGrid.align:=alClient;
FGrid.BorderStyle:=bsNone;
FGrid.ScrollBars:=ssVertical;
FGrid.FixedCols:=0;
FGrid.FixedRows:=0;
FGrid.OnTopLeftChanged:=self.TopLeftChangedHandler;
FGrid.Options:=[goFixedVertLine,
goFixedHorzLine,
goVertLine,
goHorzLine,
goRowSelect];
end;
destructor TSpkGrid.destroy;
begin
FColumns.clear;
FColumns.Free;
// Pozostałe dynamicznie utworzone komponenty są odpowiednio "zownerowane",
więc
// zostaną zwolnione automatem - w tym edity leżące na FEditPanelu.
inherited destroy;
end;
end.
</code>
With best regards -- Spook.
--
! ._______. Warning: Lucida Console sig! //) !
! || spk || www.spook.freshsite.pl / _ """*!
! ||_____|| spook at op.pl / ' | ""!
! | ___ | tlen: spoko_ws gg:1290136 /. __/"\ '!
! |_|[]_|_| May the SOURCE be with you! \/) \ ! |
|