0

I have created a new designtime component, which contains a published property Handler of type TComponent and registered it into the Tool Palette.

When i place a component of this type on my form, the property editor of the IDE shows me the property 'Handler' with a dropdown box that allows me to set this property at design time. The dropbox shows all available TComponents on the current form.

How can I restrict the list of components that is shown here (design time) to components of a certain type or with a certain property? i.e. Components that implement a certain (set of) interfaces.

I know that you can also use interface-properties, but also encountered several posts on the internet stating that this is very unstable and raises all kinds of problems.

Is there a method I can call for each of the proposed components where I can determine if they should appear in the list at design time?

Addition after the answer of @David:
Now that I've learned that TComponentProperty is what i was looking for, I also found a related question here: How to modify TComponentProperty to show only particular items on drop down list?

Community
  • 1
  • 1
Bascy
  • 2,017
  • 1
  • 21
  • 46
  • 2
    Err, by changing your `Handler` property type to that *restricted* one ? – TLama Feb 11 '15 at 12:43
  • @TLama That would be a possibility if the filtering I want to apply was that simple :-) – Bascy Feb 11 '15 at 12:50
  • 1
    Then perhaps you should [edit] your question and explain what filtering you want to apply that isn't that simple, so we know what you're attempting to do. It makes it considerably easier to provide help if we actually know what problem you're trying to solve in the first place. – Ken White Feb 11 '15 at 14:10

2 Answers2

4
  1. Derive a sub class of TComponentProperty.
  2. Override its GetValues method to apply your filter.
  3. Register this TComponentProperty as the property editor for your property.

Here is a very simple example:

Component

unit uComponent;

interface

uses
  System.Classes;

type
  TMyComponent = class(TComponent)
  private
    FRef: TComponent;
  published
    property Ref: TComponent read FRef write FRef;
  end;

implementation

end.

Registration

unit uRegister;

interface

uses
  System.SysUtils, System.Classes, DesignIntf, DesignEditors, uComponent;

procedure Register;

implementation

type
  TRefEditor = class(TComponentProperty)
  private
    FGetValuesProc: TGetStrProc;
    procedure FilteredGetValuesProc(const S: string);
  public
    procedure GetValues(Proc: TGetStrProc); override;
  end;

procedure TRefEditor.FilteredGetValuesProc(const S: string);
begin
  if S.StartsWith('A') then
    FGetValuesProc(S);
end;

procedure TRefEditor.GetValues(Proc: TGetStrProc);
begin
  FGetValuesProc := Proc;
  try
    inherited GetValues(FilteredGetValuesProc);
  finally
    FGetValuesProc := nil;
  end;
end;

procedure Register;
begin
  RegisterComponents('Test', [TMyComponent]);
  RegisterPropertyEditor(TypeInfo(TComponent), nil, 'Ref', TRefEditor);
end;

end.

This rather useless property editor will only offer you components whose names begin with A. Despite its complete lack of utility, this does illustrate the ability to filter that you desire. You'll likely want to call Designer.GetComponent(...) passing a component name to obtain the component instance, and implement your filtering based on the type and state of that component instance.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Filtering by name is *cool*, but how do you filter by component type ? How can you access each component by name (passed by `S` in the callback) ? Also, e.g. components from datamodules have the fully qualified name like e.g. `DataModule.MyComponent`, so if that's somehow easy, will that work for this case ? – TLama Feb 11 '15 at 15:17
  • 1
    @Tlama Call `Designer.GetComponent(Name)` and take it from there. I think. I don't really do any design time stuff so I'm making this up as I go along a little. Anyway, I added a word to that effect to the answer. Thanks for the Startswith tip, I always forget the name of that thing and try `LeftStr`! – David Heffernan Feb 11 '15 at 15:20
-1

As @TLama already pointed out you need to change the tpe of handler field/property.

Now in case if you want to ba able to assign specific type of component to this field/property then set this field type to the same type of that compomnent.

But if you want to be able to assign several different component types but not all components make sure that Handler field/property type is the type of the first common ancestor class/component of your desired components that you want to be able to assign to the Handler field/property.

SilverWarior
  • 7,372
  • 2
  • 16
  • 22
  • Thanks for your answer. But as in this example the first common ancestor is TComponent, the combobox will list all kinds of TComponents that aren't eligible to be used for that property. – Bascy Feb 11 '15 at 14:15
  • Are you deriving your components from other existing component like TCustomPanel for instance or directly from TComponent class? If you are deriving them directly from TComponent class you can create aditional class (dummy class) which will derive from TComponent class and then derive all of your components from that class. This will give you ability to sett that class as Handler field type. So this will make sure that object inspector would only be showing your own components since they will all be decendants of that new class. – SilverWarior Feb 11 '15 at 14:42
  • Unfortunately this won't be posible if you are deriving your components from other existing components becouse you would have to modify their own parent classes to also derive from your new dummy class which might not be posible unless you have full source code available for them. – SilverWarior Feb 11 '15 at 14:44