Delphi component design - get property from component from subproperty - delphi

I'm making a Delphi vcl component, the component class has a 'images' property which lets me select a TImagelist.
The component class also has a subproperty 'buttons' which itself has a imageindex property.
I have written a component editor for the imageindex property so that i can select a image on the buttons from the imagelist; i have done this in other components before but the problem i'm facing now is that i need to get the images property of the base class from the event in the 'buttons' subclass event.
So, the base class of the component has these properties:
property Buttons: TFlexButtons read FButtons write FButtons;
property Images: TCustomImageList read FImages write SetImages;
The buttons class has this property:
property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1;
I register a property editor in a seperate unit for the ImageIndex property, in order to pick a image but in this event i need to get the imagelist from the baseclass of the component, how do i get this property from this sub property?
function TImageIndexProperty.GetImageListAt(Index: Integer): TCustomImageList;
var APersistent: TPersistent;
begin
APersistent := GetComponent(Index);
if APersistent is TFlexButton then
Result := ??????????.Images //how do i refer to the images property of the component here?
else
Result := nil;
end;
All classes:
TFlexButton = class(TCollectionItem)
private
FWidth: Word;
FCaption: string;
FHeight: Word;
FImageIndex: TImageIndex;
procedure SetCaption(const Value: string);
procedure SetHeight(const Value: Word);
procedure SetWidth(const Value: Word);
procedure SetImageIndex(const Value: TImageIndex);
public
constructor Create(AOwner: TComponent);
destructor Destroy; override;
published
property Caption: string read FCaption write SetCaption;
property Height: Word read FHeight write SetHeight;
property Width: Word read FWidth write SetWidth;
property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1;
end;
TFlexButtons = class(TCollection)
private
function GetItem(Index: Integer): TFlexButton;
public
function Add: TFlexButton;
property Item[index: Integer]: TFlexButton read GetItem;
end;
TFlexButtonGroupBox = class(TcxGroupBox)
private
FDataLink: TFieldDataLink;
FAbout: string;
FAlignment: TAlignment;
FEnabled: Boolean;
FButtons: TFlexButtons;
FImages: TCustomImageList;
FSql: TStrings;
FAutosize: Boolean;
procedure SetAlignment(const Value: TAlignment);
function GetDataField: string;
function GetDataSource: TdataSource;
procedure SetDataField(const Value: string);
procedure SetDataSource(const Value: TdataSource);
procedure DataChange(Sender: TObject);
procedure SetEnabled(const Value: Boolean);
procedure SetImages(const Value: TCustomImageList);
procedure SetSql(const Value: TStrings);
procedure SetAutosize(const Value: Boolean);
protected
public
procedure Loaded; override;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property DataField: string read GetDataField write SetDataField;
property DataSource: TdataSource read GetDataSource write SetDataSource;
property Enabled: Boolean read FEnabled write SetEnabled;
property Autosize: Boolean read FAutosize write SetAutosize;
property About: string read FAbout write FAbout;
property Buttons: TFlexButtons read FButtons write FButtons;
property Images: TCustomImageList read FImages write SetImages;
property Alignment: TAlignment read FAlignment write SetAlignment;
property Sql: TStrings read FSql write SetSql;
end;

When exposing a collection at design time, use TOwnedCollection instead of TCollection directly. This facilitates DFM streaming without having to write extra code to enable it.
TCollectionItem has a Collection property, which in turn has an Owner method that TOwnedCollection implements. This way, you can get from a button to its owning group box in code.
Try this:
TFlexButton = class(TCollectionItem)
private
...
public
constructor Create(ACollection: TCollection); override;
end;
TFlexButtonGroupBox = class;
TFlexButtons = class(TOwnedCollection)
private
...
public
constructor Create(AOwner: TFlexButtonGroupBox); reintroduce;
...
end;
TFlexButtonGroupBox = class(TcxGroupBox)
private
...
procedure SetButtons(AValue: TFlexButtons;
public
constructor Create(AOwner: TComponent); override;
...
published
...
property Buttons: TFlexButtons read FButtons write SetButtons;
...
end;
constructor TFlexButton.Create(ACollection: TCollection);
begin
inherited;
...
end;
constructor TFlexButtons.Create(AOwner: TFlexButtonGroupBox);
begin
inherited Create(AOwner, TFlexButton);
...
end;
constructor TFlexButtonGroupBox.Create(AOwner: TComponent);
begin
inherited;
FButtons := TFlexButtons.Create(Self);
...
end;
procedure TFlexButtonGroupBox.SetButtons(AValue: TFlexButtons;
begin
FButtons.Assign(AValue);
end;
function TImageIndexProperty.GetImageListAt(Index: Integer): TCustomImageList;
begin
Result := ((GetComponent(Index) as TFlexButton).Collection.Owner as TFlexButtonGroupBox).Images;
end;

Related

How invoke the 'Edit' method of a component property on delphi programatically?

I'm building a simple component that stores the properties of a Tfont. is working properly, but I would like to implement a call to the default editor for the component property. I already searched a lot in Google and here too and tried several things, but, I could not call the 'default editor' programmatically. the component and publisher code follows:
type TMyPersistentFont = class(TComponent)
var
FOwner: TPersistent;
private
FProperties: TFont;
procedure SetProperties(const Value: TFont);
protected
function GetOwner: TPersistent; override;
public
constructor Create(AOwner: TComponent); override;
published
property Properties: TFont read FProperties write SetProperties;
end;
...
type
TMyPersistentFontEditor = class(TComponentEditor)
function GetVerbCount: Integer; override;
function GetVerb(Index: Integer): string; override;
procedure ExecuteVerb(Index: Integer); override;
//
procedure Edit; override;
end;
procedure TMyPersistentFontEditor.ExecuteVerb(Index: Integer);
begin
inherited;
case Index of
0:
begin
var FontDlg:= TFontDialog.Create(Component);
FontDlg.Font.Assign(TMyPersistentFont(Component).Properties);
try
if FontDlg.Execute then
begin
TMyPersistentFont(Component).Properties.Assign(FontDlg.Font);
Designer.Modified;
end;
finally
FontDlg.Free
end;
end;
//TPropertyEditor(TMyPersistentFont(Component).Properties).Edit; //don't works
end;
end;
Forgive me if I am duplicating the question, but, I really could not find a question that answered my question.

Update VirtualStringTree header after a component property has changed

I would like to create a component named TMyComp.
This component has associated following properties:
property VirtualStringTree: TVirtualStringTree and
property Columns: TMyCompColumns as a collection of items.
The columns from my component are the same with the header columns from associated VirtualStringTree.
What I would like to do, is to redraw at design-time the header text from VirtualStringTree when the caption is updated.
My problem is that I don't know how to trig the procedure RedrawVirtualStringTreeHeader because it's not known by class TMyCompColumns or even TMyCompColumnsItem.
TMyCompColumnsItem = class(TCollectionItem)
private
FCaption: String;
function GetPosition: Integer;
protected
function GetDisplayName: String; override;
procedure SetIndex(Value: Integer);
public
constructor Create(Collection: TCollection); override;
procedure Assign(Source: TPersistent); override;
published
property Caption: String read FCaption write FCaption;
end;
TMyCompColumns= class(TCollection)
private
FOwner: TComponent;
protected
function GetOwner: TPersistent; override;
function GetItem(Index: Integer): TMyCompColumnsItem;
procedure SetItem(Index: Integer; Value: TMyCompColumnsItem);
procedure Update(Item: TMyCompColumnsItem);
public
constructor Create(AOwner: TComponent);
function Add: TMyCompColumnsItem;
property Items[Index: Integer]: TMyCompColumnsItem read GetItem write SetItem;
end;
TMyComp = class(TComponent)
private
FColumns: TMyCompColumns;
FVirtualStringTree: TVirtualStringTree;
procedure SetMyCompColumns(const Value: TMyCompColumns);
protected
{ Protected declarations }
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Columns: TMyCompColumns read FColumns write SetMyCompColumns;
property VirtualStringTree: TVirtualStringTree read FVirtualStringTree write FVirtualStringTree;
end;
...
function TMyCompColumnsItem.GetDisplayName: String;
begin
Result:= FCaption;
RedrawVirtualStringTreeHeader; //<--- procedure not recognized!!!
end;
...
procedure TMyCompColumns.Update(Item: TMyCompColumnsItem);
begin
inherited;
//RedrawVirtualStringTreeHeader; ???or here
end;
procedure TMyComp.RedrawVirtualStringTreeHeader;
var
i: Integer;
begin
if Assigned(FVirtualStringTree) then
begin
FVirtualStringTree.Header.Options:= FVirtualStringTree.Header.Options + [hoVisible];
FVirtualStringTree.Header.Columns.Clear;
if FColumns.Count > 0 then
for i := 0 to FColumns.Count-1 do
with FVirtualStringTree.Header.Columns.Add do
begin
Text:= FColumns.Items[i].Caption;
//...
end;
end;
end;
After some searching, this is the answer:
The trigger of RedrawVirtualStringTreeHeader has been done through the FOwner inside of TMyCompColumns class.
procedure TMyCompColumns.Update(Item: TCollectionItem);
begin
inherited;
(FOwner as TMyComp).RedrawVirtualStringTreeHeader;
end;
I updated property Caption: String read FCaption write FCaption with write SetCaption and I add the procedure
procedure TMyCompColumnsItem.SetCaption(const Value: String);
begin
FCaption:= Value;
Changed(False); //---> this will trigger TMyCompColumns.Update
end;
In fact the secret was Changed(False); that trig the Update
Thanks also to open source of TVirtualStringTree component.

cxFilterControl getting Filtertext inside in a component

I have cxFilterControl what I felt with TCollectionItems (TableCollection - Field(Items). For example... Persons Table -> Person_ID, Age, Name etc...
What I need to do...
To save TcxFilterControl. FilterText to string to work with them after
I can get FilterTextSample: = cxFilterControl1.FilterText easily outside the component but generally the condition is setted by the user. I need the FilterText inside a Component
What I did...
TMySampleComp = class(TComponent, IcxFilterControl)
private
FCriteria: TcxFilterControlCriteria;
function GetPropertiesClassFromFieldType(AFieldType: TFieldType): TcxCustomEditPropertiesClass;
private
function GetCaption(Index: Integer): string;
function GetCount: Integer;
function GetCriteria: TcxFilterCriteria;
function GetItemLink(Index: Integer): TObject;
function GetItemLinkID(Index: Integer): Integer;
function GetItemLinkName(Index: Integer): string;
function GetProperties(Index: Integer): TcxCustomEditProperties;
function GetValueType(Index: Integer): TcxValueTypeClass;
public
property Captions[Index: Integer]: string read GetCaption;
property Criteria: TcxFilterCriteria read GetCriteria;
property ItemLinkNames[Index: Integer]: string read GetItemLinkName;
property ItemLinkIDs[Index: Integer]: Integer read GetItemLinkID;
property ItemLinks[Index: Integer]: TObject read GetItemLink;
property Properties[Index: Integer]: TcxCustomEditProperties read GetProperties;
property ValueTypes[Index: Integer]: TcxValueTypeClass read GetValueType;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
end;
TField = class(TCollectionItem)
private
FEditProperties: TcxCustomEditProperties;
procedure CreateProperties;
procedure DestroyProperties;
procedure RecreateProperties;
procedure SetFieldType(const Value: TFieldType);
protected
procedure SetIndex(Value: Integer); override;
function GetDisplayName: string; override;
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property EditProperties: TcxCustomEditProperties read FEditProperties;
published
end;
Everything is ok, all the Items can be reach through the TcxFilterControl -> (FilterText). Anyone can give a little advice how canI get these text (for example ... (PERSON_ID >= 12) inside a component? I need a function for it.
What I start:
procedure TdafUniqueSQLGenerate.GetFilterChangeValue(Sender:
TcxFilterCriteria;
const AFilterText: string);
var
vFilterText: string;
I: integer;
J: integer;
begin
if Sender is TcxFilterCriteria then
begin
vFilterText := Sender.FilterText;
vFilterText := StringReplace(vFilterText, '<> NULL', 'is not null', [rfReplaceAll]);
vFilterText := StringReplace(vFilterText, '= NULL', 'is null', [rfReplaceAll]);
end
else vFilterText := '';
if not vFilterText.IsEmpty then
begin
for I := 0 to Tables.Items[i].Fields.Count -1 do
Thanks for the help and sorry for my Eng!

How to Create Group Property [duplicate]

This question already has an answer here:
How do I group my component's properties in the Object Inspector?
(1 answer)
Closed 7 years ago.
I want to create a group of property (Expandable Property) I Define a Record Type and Set Type of My Property as a record. but That property dos not Appears in object inspector but on run time I can access to that property.
type
GageProperty = Record
MaxValue:Real;
Color1:TColor;
Color2:TColor;
DividerLength:Integer;
DownLimit:Real;
FloatingPoint:Integer;
Frame:Boolean;
GageFont:TFont;
GradiantStyle:GradStyle;
Height:Integer;
Width:Integer;
Left:Integer;
MinValue:Real;
NeedleColor:Tcolor;
Sector1Color:TColor;
Sector2Color:TColor;
Sector3Color:TColor;
SignalFont:TFont;
SignalNmae:String;
Step:Integer;
SubStep:Integer;
Thickness:Integer;
Top:Integer;
UpLimit:Real;
ValueUnit:String;
End;
TGasTurbine = class(TPanel)
private
{ Private declarations }
FGageProp:GageProperty;
Procedure SetGageProp(Const Value:GageProperty);
published
{ Published declarations }
Property GageProp: GageProperty Read FGageProp Write SetGageProp;
What should i Do?
Please Help me
For a structured type to be streamable and to be set in the designer, the type has to descend from TPersistent:
type
TGage = class(TPersistent)
public
MaxValue: Real;
Color1: TColor;
Color2: TColor;
procedure Assign(Source: TPersistent); override;
end;
TGasTurbine = class(TPanel)
private
FGage: TGage;
procedure SetGage(Value: TGage);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Gage: TGage read FGage write SetGage;
end;
procedure TGage.Assign(Source: TPersistent);
begin
if Source is TGage then
begin
MaxValue := TGage(Source).MaxValue;
Color1 := TGage(Source).Color1;
Color2 := TGage(Source).Color2;
end
else
inherited Assign(Source);
end;
constructor TGasTurbine.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FGage := TGage.Create;
end;
destructor TGasTurbine.Destroy;
begin
FGage.Free;
inherited Destroy;
end;
procedure TGasTurbine.SetGage(Value: TGage);
begin
FGage.Assign(Value);
end;

Collection editor does not open for a TCollection property in a TPersistent property

I've got my custom collection property which is working great when it is a direct member of my component.
But I want to move the collection property to a TPersistent propery within my component. And now comes the problem, it doesn't work: double clicking on the collection property in the object inspector normally opens the collection editor, but it does not anymore.
Fist of all - what should I pass to the contructor of the TPersistent property?
TMyCollection = class(TCollection)
constructor Create(AOwner: TComponent); // TMyCollection constuctor
...
I can't pass Self, so should I pass my persistent owner?
constructor TMyPersistent.Create(AOwner: TComponent);
begin
inherited Create;
fOwner := AOwner;
fMyCollection := TMyCollection.Create(AOwner); // hmmm... doesn't make sense
end;
I think I'm missing something. If more code is needed just please comment this post.
A TCollection's constructor does not need a TComponent, but a TCollectionItemClass.
Your collection now being a member of a TPersistent property instead of being a direct member of the component makes no difference for the constructor.
Update
What dóes differ is the ownership, but then at the TPersistent level, which should be managed by a correct implementation of GetOwner:
GetOwner returns the owner of an object. GetOwner is used by the GetNamePath method to find the owner of a persistent object. GetNamePath and GetOwner are introduced in TPersistent so descendants such as collections can appear in the Object Inspector.
You have to tell the IDE that your TCollection property is owned by the TPersistent property, which in turn is owned by the component.
The tutorial you are using has several errors regarding this implementation:
The owner of the collection is declared as TComponent, which should be TPersistent,
GetOwner is not implemented for the TPersistent property class, and
The fix shown at the end of the tutorial, stating that the TPersistent property should inherit from TComponent instead, is plain wrong; or more nicely said: is rather a workaround for not implementing GetOwner.
This is how it should look like:
unit MyComponent;
interface
uses
Classes, SysUtils;
type
TMyCollectionItem = class(TCollectionItem)
private
FStringProp: String;
protected
function GetDisplayName: String; override;
public
procedure Assign(Source: TPersistent); override;
published
property StringProp: String read FStringProp write FStringProp;
end;
TMyCollection = class(TCollection)
private
FOwner: TPersistent;
function GetItem(Index: Integer): TMyCollectionItem;
procedure SetItem(Index: Integer; Value: TMyCollectionItem);
protected
function GetOwner: TPersistent; override;
public
constructor Create(AOwner: TPersistent);
function Add: TMyCollectionItem;
function Insert(Index: Integer): TMyCollectionItem;
property Items[Index: Integer]: TMyCollectionItem read GetItem
write SetItem;
end;
TMyPersistent = class(TPersistent)
private
FOwner: TPersistent;
FCollectionProp: TMyCollection;
procedure SetCollectionProp(Value: TMyCollection);
protected
function GetOwner: TPersistent; override;
public
procedure Assign(Source: TPersistent); override;
constructor Create(AOwner: TPersistent);
destructor Destroy; override;
published
property CollectionProp: TMyCollection read FCollectionProp
write SetCollectionProp;
end;
TMyComponent = class(TComponent)
private
FPersistentProp: TMyPersistent;
procedure SetPersistentProp(Value: TMyPersistent);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property PersistentProp: TMyPersistent read FPersistentProp
write SetPersistentProp;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyComponent]);
end;
{ TMyCollectionItem }
procedure TMyCollectionItem.Assign(Source: TPersistent);
begin
if Source is TMyCollectionItem then
FStringProp := TMyCollectionItem(Source).FStringProp
else
inherited Assign(Source);
end;
function TMyCollectionItem.GetDisplayName: String;
begin
Result := Format('Item %d',[Index]);
end;
{ TMyCollection }
function TMyCollection.Add: TMyCollectionItem;
begin
Result := TMyCollectionItem(inherited Add);
end;
constructor TMyCollection.Create(AOwner: TPersistent);
begin
inherited Create(TMyCollectionItem);
FOwner := AOwner;
end;
function TMyCollection.GetItem(Index: Integer): TMyCollectionItem;
begin
Result := TMyCollectionItem(inherited GetItem(Index));
end;
function TMyCollection.GetOwner: TPersistent;
begin
Result := FOwner;
end;
function TMyCollection.Insert(Index: Integer): TMyCollectionItem;
begin
Result := TMyCollectionItem(inherited Insert(Index));
end;
procedure TMyCollection.SetItem(Index: Integer; Value: TMyCollectionItem);
begin
inherited SetItem(Index, Value);
end;
{ TMyPersistent }
procedure TMyPersistent.Assign(Source: TPersistent);
begin
if Source is TMyPersistent then
CollectionProp := TMyPersistent(Source).FCollectionProp
else
inherited Assign(Source);
end;
constructor TMyPersistent.Create(AOwner: TPersistent);
begin
inherited Create;
FOwner := AOwner;
FCollectionProp := TMyCollection.Create(Self);
end;
destructor TMyPersistent.Destroy;
begin
FCollectionProp.Free;
inherited Destroy;
end;
function TMyPersistent.GetOwner: TPersistent;
begin
Result := FOwner;
end;
procedure TMyPersistent.SetCollectionProp(Value: TMyCollection);
begin
FCollectionProp.Assign(Value);
end;
{ TMyComponent }
constructor TMyComponent.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPersistentProp := TMyPersistent.Create(Self);
end;
destructor TMyComponent.Destroy;
begin
FPersistentProp.Free;
inherited Destroy;
end;
procedure TMyComponent.SetPersistentProp(Value: TMyPersistent);
begin
FPersistentProp.Assign(Value);
end;
end.
But may I say that you can also inherit from TOwnedCollection, which makes the use and the declaration of TMyCollection much simpler:
TMyCollection = class(TOwnedCollection)
private
function GetItem(Index: Integer): TMyCollectionItem;
procedure SetItem(Index: Integer; Value: TMyCollectionItem);
public
function Add: TMyCollectionItem;
function Insert(Index: Integer): TMyCollectionItem;
property Items[Index: Integer]: TMyCollectionItem read GetItem
write SetItem;
end;

Resources