7

I want my component which will be non-visual to have its published properties under a category not on the top level of the Object Inspector.

Take the below example:

enter image description here

type
  TMyComponent = class(TComponent)
  protected
    function GetSomeValue: string;
    function GetSomeValueExt: string;
  published
    property SomeValue: string read GetSomeValue;
    property SomeValueExt: string read GetSomeValueExt;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('My Component', [TMyComponent]);
end;

function TMyComponent.GetSomeValue: string;
begin
  Result := 'test';
end;

function TMyComponent.GetSomeValueExt: string;
begin
  Result := 'extended..';
end;

How do I get my component to register in the Object Inspector with SomeValue and SomeValueExt under a category named something like MyProperties?

Illustration:

enter image description here

My component could potentially have a lot of published properties and I would rather they went under there own level subcategory of the Object Inspector to keep it away from the common properties such as Name and Tag.

Thanks :)

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • Are you talking about the Category term that is used by the Object Inspector? That is the feature where you right click on OI and select "View by Category". – David Heffernan Feb 24 '12 at 16:41
  • Say as example you click on TForm or other component, some properties are under categories such as Anchors, BorderIcons and Font etc. I want some of my properties to have there own parents if that makes sense. –  Feb 24 '12 at 16:45
  • `Anchors` and `BorderIcons` are sets. You don't want that. The `Font` property is a class. So you could wrap your sub-properties in a class and then get the behaviour you desire for free. – David Heffernan Feb 24 '12 at 16:49
  • So I could put SomeValue and SomeValueExt in there own class which is parent to TMYComponent, Is that right? –  Feb 24 '12 at 16:51
  • Replace a screenshot with one which illustrates category view then. Or, we dont get what the XY you really want. – OnTheFly Feb 24 '12 at 16:51
  • @Blobby Yes that is correct. I believe that a published property that is a class will present itself in the OI the way you desire. – David Heffernan Feb 24 '12 at 16:55
  • @user539484 I added a illusration image. –  Feb 24 '12 at 17:00
  • @Blobby, yup. Meanwhile, [`RegisterPropertiesInCategory`](http://docwiki.embarcadero.com/VCL/en/DesignIntf.RegisterPropertiesInCategory) is the answer to the other interpretation :-) – OnTheFly Feb 24 '12 at 17:06
  • Thanks @user539484 I will look at RegisterPropertiesInCategory it might be in the help files :) –  Feb 24 '12 at 17:08
  • @Blobby, its is in *component writer's guide*, anyway, added a documentation link for you. – OnTheFly Feb 24 '12 at 17:15
  • @user539484 Thanks for the link, I will be sure to the check out this writers guide you mentioned too. –  Feb 24 '12 at 17:29

1 Answers1

17

Create a class that has those properties, and then give your component a single property of that class type. The property class should be a TPersistent descendant:

type
  TComponentProperties = class(TPersistent)
  private
    function GetSomeValue: string;
    function GetSomeValueExt: string;
  published
    property SomeValue: string read GetSomeValue;
    property SomeValueExt: string read GetSomeValueExt;
  end;

  TMyComponent = class(TComponent)
  private
    FProperties: TComponentProperties;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Properties: TComponentProperties read FProperties;
  end;

The component owns its property object, so it needs to be created and destroyed:

constructor TMyComponent.Create;
begin
  inherited;
  FProperties := TComponentProperties.Create;
end;

destructor TMyComponent.Destroy;
begin
  FProperties.Free;
  inherited;
end;

With that code, the component's properties should now be listed under the "Properties" item. It's not a category, though. Categories are something else entirely. Categories don't re-arrange the component; they just change how the properties are displayed in the Object Inspector. Note that with my code, TMyComponent doesn't have any SomeValue property anymore. Instead, it just has one property, Properties, and that object has other properties. Consider whether that's really how you want consumers of your component to have to access it.


If the Properties property is not read-only, then it needs to have a property setter; you cannot have it write directly to FProperties. Write it like this:

procedure TMyComponent.SetProperties(const Value: TProperties);
begin
  FProperties.Assign(Value);
end;

That also requires that you override the Assign method to do the right thing:

procedure TComponentProperties.Assign(Other: TPersistent);
begin
  if Other is TComponentProperties then begin
    SomeValue := TComponentProperties(Other).SomeValue;
    SomeValueEx := TComponentProperties(Other).SomeValueEx;
  end else
    inherited;
end;

We're also assuming that the property object's properties aren't read-only, either. When the property object's properties change, the owning object will probably want to know about it, so it should have an event that the component assigns a value to. When the properties change, they'll trigger the event.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • Thanks for the information, I had a feeling categories might not best explain this. I use Delphi for personal use so it won't really be anyone else using my component but it is always worth considering if in the future someone were to use it. Thanks :) –  Feb 24 '12 at 17:07
  • 1
    When I said "consumers of your component," that *included* you. Do *you* want to type `Properties.SomeValue` all the time? – Rob Kennedy Feb 24 '12 at 17:09
  • Don't worry I was going to, give me chance to check it all out and make sure I understand it first! :) –  Feb 24 '12 at 17:16
  • @RobKennedy the properties will be read-only, but again thanks for including this extra information - it will be useful should I need to write to the properties. I have tried this out just now and it works as I had hoped, it actually seems a lot simpler than I thought - thanks to a well written example and easy to follow information. –  Feb 24 '12 at 17:28
  • iirc the line `SetSubcomponent(FProperties);` in the `constructor TMyComponent.Create;` is also required – mjn Feb 24 '12 at 19:06
  • 1
    I doubt it, @Mjn. It's not a component. Also, if it's required, the retirement is new; Delphi 2005 didn't need that. – Rob Kennedy Feb 24 '12 at 19:14
  • @mjn Rob's doubt is well grounded. Like the name already says, that's only required for sub components. You might need to implement [GetOwner](http://stackoverflow.com/a/6980697/757830) though, depending on the type and depth of sub properties. – NGLN Feb 24 '12 at 21:08
  • @Rob Properties without setter (whether readonly by design or not) are *not* visible in the Object Inspector, unless _Show read only properties_ is checked in the Environment Options (which is disabled by default). – NGLN Feb 24 '12 at 21:10