Information
I seem absolutely clueless as to how I should manage my component packages and install custom property editors. I have spent the last week or so looking at examples and tutorials, I must clearly be doing something wrong as my property editors never show up in the Object Inspector and I require step by step instructions to try and solve this frustration.
Component Package
I currently have one package, and it is set to designtime and runtime in the options. Added to this package are the units to my components (ie, MyButton, MyListBox etc) - Additionally I have a unit containing the RegisterComponents procedure. I can build and install this now, add my components to a new project and run them without any problems.
Adding a custom PropertyEditor
It gets tricky and confusing for me now that I want to introduce a property editor to my components from the package above.
TMyButton for example is a custom button with my own paint methods, it already has published properties to allow changing the appearance of the button - In addition there is also a published list to allow selecting preset appearance settings, example:
TMyButtonStyle = (bsStyle1, bsStyle2, bsStyle3)
I want to take away the list of preset styles and instead add a property to the Object Inspector called 'PresetStyles'. This property will be of paDialog, I want to a show a form where I can visually see the different button styles - a more graphical way of selecting a preset style then from a simple list.
Which packages do I need
If I understand, you need to split designtime packages and runtime packages?
Does this mean I need two register units, one for the components and one for the Property Editors?
I am not full sure how to manage this, as I said my current package is designtime and runtime, I am not sure what package type I should split it into. If I make a new designtime package for the Property Editors, and make the other package runtime only I lose the install button. Even writing this now is confusing me.
Installing the PropertyEditor
One of the articles I tried following was one from this page: http://www.delphisources.ru/pages/faq/master-delphi-7/content/LiB0097.html
The only thing I changed was add my own dialog form, and change the register code from:
RegisterPropertyEditor(TypeInfo(string), TMdSoundButton, 'SoundUp', TSoundProperty);
to
RegisterPropertyEditor(TypeInfo(string), TMyButton, 'PresetStyles', TSoundProperty);
I know it still says TSoundProperty, I just left it like that until I could get it working then I would change the class name.
Conclusion
What is the correct way of splitting/managing packages between actual components and the design side ie PropertyEditors?
The PropertyEditor never appears on TMyButton in the Object Inspector and I have a feeling it must be something to do with not configuring the packages correctly or something.
I would really appreciate some kind of assistance here, even a link to a really good tutorial guide or something as nothing I am doing seems to work, even using a lot of the examples are not working for me.
I currently have one package, and it is set to designtime and runtime in the options.
To implement custom property/component editors, you MUST separate your code into two packages - one runtime only package containing just the implementation code for the components themselves, and one designtime only package that contains just the implementation code for the component registrations and custom editor(s). The designtime package needs to specify the runtime package, and the designide package, in its Requires list. The runtime package is what gets compiled into executables. The designtime package is what the IDE uses to get your components to appear in the Component Palette and Form Designer and interact with them.
Does this mean I need two register units, one for the components and one for the Property Editors?
No. The runtime package should have no registrations at all. That belongs in the designtime package instead. You can have a single Register() function in the designtime package that registers everything.
If I make a new designtime package for the Property Editors, and make the other package runtime only I lose the install button.
You cannot install a runtime package into the IDE, only a designtime package.
One of the articles I tried following was one from this page: http://www.delphisources.ru/pages/faq/master-delphi-7/content/LiB0097.html
The only thing I changed was add my own dialog form, and change the register code from:
RegisterPropertyEditor(TypeInfo(string), TMdSoundButton, 'SoundUp', TSoundProperty);
to
RegisterPropertyEditor(TypeInfo(string), TMyButton, 'PresetStyles', TSoundProperty);
Does your TMyButton component actually define a PresetStyles property that is a String type? You cannot define a property editor for a property that does not exist.
Without knowing exactly how your PresetStyles property is implemented in the component itself and what it represents, it probably does not make much sense to invoke a popup Dialog for a String property (except maybe for things like filenames and such). Based on your description, it probably makes more sense to implement a component editor instead of a property editor, and leave your existing TMyButtonStyle property alone to use the IDE's default editor for enum properties. To invoke your popup dialog, the component editor would allow the user to right-click on the component itself and choose "Edit" (or whatever other string value you decide to name it) from the popup menu, or just double-click on the component, in the Form Designer. You can then display and edit the component as desired, and assign any changes to the component when the dialog is closed.
The PropertyEditor never appears on TMyButton in the Object Inspector and I have a feeling it must be something to do with not configuring the packages correctly or something.
It is hard to know for sure since you did not show any of your actual code yet.
If I understand, you need to split designtime packages and runtime packages?
Does this mean I need two register units, one for the components and one for the Property Editors?
No. One registration unit that registers both the components ánd the property editors is enough, as long as that registration unit isn't used at runtime (which normally isn't). This applies to the unit containing the property editor too. Prevent using DesignEditors.pas at runtime, and then you're fine.
Component Package
It is OK to have one package for both today. In the past it made sense to split them if an application was
distributed with runtime packages to reduce the total size. I like to split them up to now to
seperate the implementation and the design interface.
Which packages do I need
If you have two packages the runtime packages must be referenced in 'Requires' in the design time package.
Therefore you only have to register the design time package. The runtime package ist implicit loaded into the IDE.
In your case with one package you have to register this.
Installing the PropertyEditor
There is something wrong with the first parameter. It must be the typeinfo of the property.
RegisterPropertyEditor(TypeInfo(TMyButtonStyle), TMdSoundButton, 'PresetStyles', TSoundProperty);
TSoundProperty should be a descand from TEnumProperty.
Conclusion
Go on with one package. The property should appear with the changed code. If not check that the property is not readonly.
Related
I'm writing a package for a custom component. This package allows the user/developer to either include numerous pre-written units in their project which are explicitly designed to work with this custom control, or write their own to do the same as one of the existing ones. Think of it like a "plugin" where code is implemented in its own unit, which inherits a particular object and does custom implementation of that common object.
Each "plugin" unit has an initialization clause at the bottom which registers this class with a global list. So long as that unit is "used" anywhere in the project, it gets registered into a global list, which this custom control makes use of to populate a list of all possible "plugin" units.
For example, this custom control has a property:
property PluginIndex: Integer read FPluginIndex write SetPluginIndex;
Internally, this index corresponds with one of the "plugins" which was registered from within the initialization section of its corresponding unit.
These "plugins" are registered via a global object which is instantiated like this upon first use:
function Plugins: TMyPluginList;
implementation
var
_Plugins: TMyPluginList;
function Plugins: TMyPluginList;
begin
if _Plugins= nil then
_Plugins:= TMyPluginList.Create;
Result:= _Plugins;
end;
The difficulty is that, since the control is implemented within a package (and installed into IDE), yet the "plugin" units are "used" from within another project, the package doesn't pick up the presence of these units, and therefore this custom control is also unable to access the global list, since it's in an entirely different context.
When interacting with this control on the form in design-time, if I set this PluginIndex in design-time, it fails because the control hasn't had any "plugins" register themselves in the global list. But, in run-time, assigning that same property works just fine.
How do I do this in a way that my application which uses this custom control (which relies on client-uses of each "plugin" unit) is able to actually detect these units used?
EDIT
The end goal of what I'm doing is to allow this single custom control to be simply dropped on the form, then have specific units "used" in that same form, and finally on this single custom control instance, choose the index of which of those "plugins" to actually use at any given time. I'm trying to encapsulate and automate as much of the process as possible, making it easier for a developer to simply drop this custom-control and not have to write a bunch of other code to make it work.
EDIT
I realize that this may be interpreted as these "plugin" units being a part of the package. That is not the case. Although currently I have no choice but to add these "plugin" units to the package in order to make design-time work right, I don't want it to be a part of the package, and rather up to the developer using this custom control. They can add a combination of pre-made "plugin" units as well as custom-made ones, simply by "using" them in their application.
I'm dealing with creating a number of components at runtime and setting their methods. Is there a way to add the declartion and definition stubs for a component without adding it to the form and clicking the corresponding event on the events tab?
I know I can add a component temporarily to create the stub, or sometimes the help files list the declaration. But there are a lot of features in the IDE that I have not found or used.
Replying to comments:
What I do currently is workable I just wondered if there was a shortcut I was missing. To reiterate as some did not understand; I am using component AwesomeComponent in my application. I only need to create instances of AwesomeComponent at runtime, so it is not on my form (or perhaps is non-visual). AmesomeComponent has an event DoSomething. That event has a number of parameters. In order to assign AmesomeComponent.OnDoSOmething at runtime, I need to code the event, but I don't have direct access to the procedure stub, I have to find it somewhere in documentation or create it on a dummy form to cut and paste it. I didn't know if there was an IDE feature that was smart enough to say "Ok, I recognize this name (AwesomeComponent.OnDoSOmething) as an event and create a stub for it. It must do essentially the same thing when double-clicking on the Events tab in the IDE.
Like the title says. I want to add an Object Inspector to my form at rum time, and when I click other components, it should have two way data binding with them.
That is, if I, for instance, change the top property of a TPanel in the object inspector, then the panel should move; and, if I drag the panel, the object inspector should reflect its Top & Left properties.
Of course, I mean this to work for all proprieties, and select a new component each time the user clicks one at run time.
I don't even know where to start :-(
[Update] I do know, however, that I would strongly prefer to use Delphi components, and nothing 3rd party
No, you cannot use the Object Inspector in your own code. It's a proprietary part of the IDE, and it is not available as source or a component. It never has been, and I highly doubt it will ever be. It's also integrated with the rest of the IDE (the Code Editor and Form Designer), neither of which are available as components either. It's functionality is contained in packages that are design-time only, and are not licensed for use outside the IDE (you can't use them in your application).
In addition, most of the property editors that are invoked by the Object Inspector are also implemented in design-time only packages that wouldn't be available to your application either.
Now this is a very puzzling matter I hope you can help me.
I have a custom component derived from TCustomControl that is included in a package. This package is correctly compiled and installed in D5. The package also requires the use of another library of components the ImageEn library.
I have an application that uses this component, Now until some day ago everything seemed to be fine, but now, after some recent changes to the component (mostly changing the name of the types it uses) I am having a very weird behavior going on. On the form where this component is placed Delphi will create I would say just for no reason a TImageEnIo component (from the ImageEn library) that was not even included in the dfm of the form.
Even more puzzling is the fact that this added imageenio component has no name, thus causing me a lot of trouble when I try to run the application (run-time error). If I delete this component it will reappear as soon as something is updated (for example if I switch between the text and the form of the dfm). Delphi just adds this thing on any form , where I use my custom component. This happens only when my custom component is present, other comps of the package do not generate this problem.
I noticed that it creates 1 of these empty TImageenio for each custom component I have on the form, and moreover if I switch back and forth between the text and the form views of the dfmit will add another imageenio component each time I do this.
Finally the problem is not dependent from the application as it will happen with any application as long as I add my custom component on a form.
Now I have been recompiling everything the imageen and my package many times, I have even tried to restore an older version of the component but the problem persisted (I am quite sure I hadn't experience it with the older version), I have tried anything conceivable but I cannot figure it out. I am at a complete loss, please advice If you need more info please ask, I will try to explain better.
Many Thanks to those who will take the time to answer.
It would seem that the problem was introduced by recent changes in your custom component.
You should use your revision control system to go back to a known good version. Then advance to intermediate versions of the code, perhaps using a binary search for efficiency. Once you've isolated the revision which introduced the bug then you should be able to study the code changes in that revision and identify the problem.
Wild guess: Your custom component directly or indirectly instantiates a TImageEnIo with the wrong Owner. Instead of using Self (i.e. the custom component instance) it uses its Owner property, which points to the form it is sitting on.
The first thing I would try would be to start a new application, create one of your custom controls at run time and put breakpoints on the third party source code where the TImageEnIo component is instantiated, constructor of the component would be fine. If the design time behavior is exhibited at also run-time, the breakpoint would be hit and you would be able to figure out the code part creating the component from the stack trace.
Otherwise, it is possible to debug design-time behavior by debugging the IDE with another instance of the IDE, but I don't know how it can be done with D5.
I have a base form with descendents. The base form has a TElRelDataSource on it (from ElPack v4.00). When I open the descendent form I get an error "cannot inherit from (parent form). Contains a component that does not support inheritance". This is with the ancestor forms open (there are two), to avoid the "ancestor for (parent form) not found" error.
Delphi 7 doesn't (AFAIK) support finalising classes, but this seems to be a standard error message. It's in the index for "Tomes of Delphi: Troubleshooting" but I don't have a copy of that and I'm reluctant to spend $US60 to read an entry like "this is why you should avoid VFI" (being pessimistic about the contents of the book). Other than that, no references in Google or Bing.
This is in code that I've recently inherited from someone who left a while ago and I'm the first person to change it since they left. Which means I can't ask how he created the offending form.
So, how do I get around this error message?
You got yourself into an interesting problem. First of all, you say the parent form already includes the offending DataSource, so you've got to ask yourself a question: How did the original developer manage to put the dataset on the parent form and then create child forms, if creating child forms is rejected by the datasource?
The original code didn't have the TElRelDataSource
This implies you're trying to add it your self. Don't add it, find an other solution that doesn't rely on the TElRelDataSource.
The original code did have it!
The original was probably compiled against a different version of TElRelDataSource, one that did allow inheritance. "Inherability" is controlled by the "csInheritable" component style. That is, if the component doesn't include "csInheritable" in ComponentStyle, the Delphi IDE will not allow visual inheritance. You have a number of options:
Use a different version of TElRelDataSource, maybe the original was compiled against a NEWER version of TElRelDataSource. If you are using the latest TElRelDataSource, the author probably decided it's code can't handle visual inheritance so he/she removed the csInheritable flag from ComponentStyle!
Try making your own derived component and set that style yourself. This is easily done, but if TElRelDataSource's author removed the csInheritable flag for a reason, you'll probably get into trouble.
Normally HeartWare's idea would work just fine; Unfortunately it's not that easy with a DATA SOURCE component: I assume you have DB-Aware controls linked to that data source. If you create the DataSource from code, you'll need to re-create the connections to data-aware controls as well, and that's going to take a lot of "if-s" (or clever use of RTTI).
You may try moving the DataSource to a TDataModule. This is probably more trouble then it's worth it.
One option would be to remove the offending component from the form and allocate it dynamically in the FormCreate event. Depending on how many properties the (presumed) non-visual Data Source component has, this could be fairly simple.