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;
Related
I have written a Delphi component that has a property of type TStrings. All works well except that when the String List Editor is launched, the "Code Editor" button is disabled. Anyone know what I need to set to allow this?
Perhaps this is due to being called from the collection editor?
The entire component is is about 80 lines so I put it all here. It is a VCL component.
// Simple example of of creating a OwnedCollection of TStrings
unit TextStorageMin;
interface
uses
System.Classes, System.SysUtils, Winapi.Windows, System.Generics.Collections;
type
// Storage class to store TStrings
TStorageStrings = class(TCollectionItem)
private
FStrings: TStrings;
procedure SetStrings(const Value: TStrings);
public
published
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
// Why, when this is brought up in the Strings List Editor, is
// the "Code Editor" not enabled.
property Strings: TStrings read FStrings write SetStrings;
end;
// Just simple Owned Collection
TStorageList = class(TOwnedCollection);
// This our component.
TTextStorageMin = class(TComponent)
private
FStorageList: TStorageList;
public
published
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Items: TStorageList read FStorageList write FStorageList;
end;
procedure Register;
implementation
// Register it
procedure Register;
begin
RegisterComponents('CompDev', [TTextStorageMin]);
end;
{ TTextStorage }
constructor TTextStorageMin.Create(AOwner: TComponent);
begin
inherited;
FStorageList := TStorageList.Create(AOwner, TStorageStrings);
end;
destructor TTextStorageMin.Destroy;
begin
FStorageList.Free;
inherited;
end;
{ TStorageStrings }
constructor TStorageStrings.Create(Collection: TCollection);
begin
inherited;
FStrings := TStringList.Create;
end;
destructor TStorageStrings.Destroy;
begin
FStrings.Free;
inherited;
end;
procedure TStorageStrings.SetStrings(const Value: TStrings);
begin
FStrings.Assign(Value);
end;
Your main component is coded all wrong. It is completely mismanaging the ownership of the TStorageList object. It is assigning the wrong Owner to the object, and there is no property setter implementee to avoid a memory leak and taking ownership of an external object (in this case, one created and destroyed by the IDE at design-time).
Also, your TStorageStrings class is missing an overload of Assign() (or AssignTo()), which also plays into the above mismanagement.
The code should look more like this instead:
// Simple example of of creating a OwnedCollection of TStrings
unit TextStorageMin;
interface
uses
System.Classes;
type
// Storage class to store TStrings
TStorageStrings = class(TCollectionItem)
private
FStrings: TStrings;
procedure SetStrings(const Value: TStrings);
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
procedure Assign(ASource: TPersistent); override;
published
property Strings: TStrings read FStrings write SetStrings;
end;
// Just simple Owned Collection
TStorageList = class(TOwnedCollection);
// This our component.
TTextStorageMin = class(TComponent)
private
FStorageList: TStorageList;
procedure SetItems(const Value: TStorageList);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Items: TStorageList read FStorageList write SetItems;
end;
procedure Register;
implementation
// Register it
procedure Register;
begin
RegisterComponents('CompDev', [TTextStorageMin]);
end;
{ TTextStorage }
constructor TTextStorageMin.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FStorageList := TStorageList.Create(Self, TStorageStrings);
end;
destructor TTextStorageMin.Destroy;
begin
FStorageList.Free;
inherited;
end;
procedure TTextStorageMin.SetItems(const Value: TStorageList);
begin
FStorageList.Assign(Value);
end;
{ TStorageStrings }
constructor TStorageStrings.Create(Collection: TCollection);
begin
inherited Create(Collection);
FStrings := TStringList.Create;
end;
destructor TStorageStrings.Destroy;
begin
FStrings.Free;
inherited;
end;
procedure TStorageStrings.Assign(ASource: TPersistent);
begin
if ASource is TStorageStrings then
FStrings.Assign(TStorageStrings(ASource).Strings)
else
inherited;
end;
procedure TStorageStrings.SetStrings(const Value: TStrings);
begin
FStrings.Assign(Value);
end;
end.
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.
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;
I'm trying to create an object structure where one object contains a list of other objects. I have a base class for both of these, similar to TCollection/TCollectionItem but a very custom implementation.
type
TMyItemBase = class;
TMyListBase = class;
TMyItemBaseClass = class of TMyItemBase;
TMyItemBase = class(TObject)
private
FOwner: TMyListBase;
public
constructor Create(AOwner: TMyListBase);
destructor Destroy; override;
property Owner: TMyListBase read FOwner;
end;
TMyListBase = class(TMyObjectBase)
private
FItems: TList;
FItemClass: TMyItemBaseClass;
function New: TMyItemBase;
function GetItem(Index: Integer): TMyItemBase;
public
constructor Create(AOwner: TMyMainObject; const ItemClass: TMyItemBaseClass);
destructor Destroy; override;
function Count: Integer;
procedure Clear;
property Items[Index: Integer]: TMyItemBase read GetItem; default;
end;
implementation
{ TMyItemBase }
constructor TMyItemBase.Create(AOwner: TMyListBase);
begin
FOwner:= AOwner;
end;
destructor TMyItemBase.Destroy;
begin
inherited;
end;
{ TMyListBase }
constructor TMyListBase.Create(AOwner: TMyMainObject; const ItemClass: TMyItemBaseClass);
begin
inherited Create(AOwner);
FItems:= TList.Create;
FItemClass:= ItemClass;
end;
destructor TMyListBase.Destroy;
begin
Clear;
FItems.Free;
inherited;
end;
procedure TMyListBase.Clear;
begin
while FItems.Count > 0 do begin
TMyItemBase(FItems[0]).Free;
FItems.Delete(0);
end;
end;
function TMyListBase.Count: Integer;
begin
Result:= FItems.Count;
end;
function TMyListBase.GetItem(Index: Integer): TMyItemBase;
begin
Result:= TMyItemBase(FItems[Index]);
end;
function TMyListBase.New: TMyItemBase;
begin
Result:= FItemClass.Create(Self);
FItems.Add(Result);
end;
(Pseudo code, sorry if there are any typos)
Problem is, when a new item is created (via TMyListBase.New), the object is successfully created, but the inheritance and all its fields are not (The inherited object's constructor is never even called)...
type
TMyItem = class;
TMyItems = class;
TMyItem = class(TMyItemBase)
private
//various unrelated fields
public
constructor Create;
destructor Destroy; override;
//various unrelated properties
end;
TMyItems = class(TMyListBase)
private
function GetItem(Index: Integer): TMyItem;
function New: TMyItem;
public
constructor Create(AOwner: TMyMainObject);
destructor Destroy; override;
property Items[Index: Integer]: TMyItem read GetItem; default;
end;
implementation
{ TMyItem }
constructor TMyItem.Create;
begin
inherited;
//Initialize some fields
end;
destructor TMyItem.Destroy;
begin
//Destroy some fields
inherited;
end;
{ TMyItems }
constructor TMyItems.Create(AOwner: TMyMainObject);
begin
inherited Create(AOwner, TMyItem);
end;
destructor TMyItems.Destroy;
begin
inherited;
end;
function TMyItems.GetItem(Index: Integer): TMyItem;
begin
Result:= TMyItem(inherited Channels[Index]);
end;
function TMyItems.New: TMyItem;
begin
Result:= TMyItem(inherited New);
end;
It appears to be something wrong with the New function, but I cannot figure it out. Even though I'm creating the item as its intended item class, it gets further treated as if it were the base class, and none of the inherited members are accessible (giving access violations) because the inherited constructor is never called.
What am I overlooking / doing wrong here?
Your TMyItemBase.Create() constructor needs to be declared as virtual, and then descendant classes need to override it. This is important when constructing objects using metaclass types. For example:
type
TMyItemBase = class(TObject)
...
public
constructor Create(AOwner: TMyListBase); virtual;
...
end;
constructor TMyItemBase.Create(AOwner: TMyListBase);
begin
inherited Create;
FOwner := AOwner;
end;
type
TMyItem = class(TMyItemBase)
...
public
constructor Create(AOwner: TMyListBase); override;
...
end;
constructor TMyItem.Create(AOwner: TMyListBase);
begin
inherited Create(AOwner);
...
end;
Try to use generics!
type
TMyItem = class
//Implement your class here
end;
//Use it now!
var
MyItemsList:TObjectList<TMyItem>;
// or implement your generic class customization:
type
TVeryMyItemsList = class(TObjectList<TMyItem>)
end;
// Or implement your generic class
type
TMyGeneric<T> = class(TObjectList<T>)
end;
//where T is any type
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;