hi to all In advance Sorry for my english.
I'm explain my problem. For an OpenGL library. I have a generic Shader component (TGLCustomGLSLShader). I'd like to make a Shader Library collection. At this time i can add TGLCustomGLSLShader in my collection. But i'd like also add descendant of TGLCustomGLSLShader. for Example : I create specific shader like TGLBumpMapShader, a TGLWaterShader ect.... with differents custom properties in each and are not present in the base class. I'd like to add this new classes in my collection and view there custom properties at design-time. It is possible ?
How can make this ? Have you some links ? I tried to search with google, but i don't find any clues.
Best regards
There are two ways to implement this with full design-time and DFM support:
Have the common base class (TGLCustomGLSLShader) derive from TCollectionItem, and then create a TOwnedCollection published property in your main Library component to hold them. Users will not be able to drop shaders on the Form at design-time, but the native collection editor should be able to delete/edit the shader objects normally. You can create a custom design-time editor that allows users to create instances of your shader classes, passing the TOwnedCollection object to their constructor. You will also have to implement custom DFM streaming for the Library component so it will create instances of the appropriate shader classes when loading a DFM, otherwise it will create TGLCustomGLSLShader objects instead of derived objects.
Have the common base class derive from TComponent, thus allowing users to drop shaders on the Form and configure their properties like normal components. Then, in your Library component, create a TOwnedCollection published property, and define a custom TCollectionItem class for it that has a published TGLCustomGLSLShader property. This will allow the user to use the native collection editor to add items and then manually link them to the desired shader components as needed. While this requires more objects and more user setup, it is friendlier on the native design-time editors and DFMs, as it allows default behaviors to act normally and does not require any custom design-time editors to manage the objects.
Related
Hi I'm developing a TControl descendant, lets name it THTMLBaseControl, at runtime that control only generates and output HTML code based on the settings of that control, so all the additional properties of the base TControl class and methods, including Windows Messaging system is really not used at runtime and causes memory overhead.
I need that control to be inherited from TControl so at design time I can use all the IDE designer stuff.
But at runtime almost all of those properties that at desingtime are needed i dont need them.
I also have all my controls inherited from that THTMLBaseControl, so creating a wrapper class per control class is not an option because it will duplicate the code a lot.
So what i need is something that at runtime, before the class is instantiated I can change the parent class so it will be instantiated based on another TControl-like class, maybe named TmyBaseControl inherited from "TComponent" as TControl Does, but that will not have all that TControl memory overhead and will only have the properties and methods needed by my THTMLBaseControl.
I really dont have a GUI at rutime is a web server that will serve only HTML, is some thing that intraweb and Raudus do, but always the issue is that all are based on TControl, so they have to be created at run time and generate a lot memory and process overhead that is not used. and maybe there could a solution so any THTMlBaseControl descendant instantiated at runtime will inherit the all properties and methods from TmyBaseControl and not from TControl.
I have seen there are ways to hack the VMT but maybe there are other solution and have not seen it. I already done changing the NewInstance, ClassParent and TnstanceSize class methods but i have to specify from which class and obviously i have to do the same steps per each inherited THTMLBaseControl class
And for the sake of all:
This is just a doubt, I need the components to be controls like TEdit, TPanel, visible and editable by the designer IDE I even could create my own TControl class but I was just thinking if I can reuse the code already existing.
Regards
You cannot change the class a run time. Once an object is instantiated, its class is fixed. You could hack the object to change its VMT pointer, making it refer to a different class, but that would still not address your main concern, which is memory usage — even if you change the VMT pointer, all the memory for the object has already been allocated; changing the VMT pointer doesn't magically make the object occupy less memory.
The first thing you could do is stop descending from TControl. As you've noted, you don't need any of the things it provides. All you want is something you can drop on a form at design time to set its properties. For that, all you need is TComponent, so make that your base class instead of TControl. Then you'll get something more like TTimer, which has no GUI. Once you've done that, you no longer need TForm, either. Instead, you can put your component on a TDataModule, which is specifically designed for managing non-visual components at design time.
1- Why is it necessary to name the methods of a class in a dwsunit this way?
dwsUnitClasses'ClassName''MethodName'Eval(Info: TProgramInfo;
var ExtObject: TObject)
2- Is there a link that must be done between the method defined in a dwsunit and its code?
coz it is not working with me. I took the demo attached with the DWscript (Custom Classes) and made something that looks like it but it is just not functioning.
No it's not necessary, that's just the format the IDE uses for auto-generated event names.
Usually you define script classes, their methods in the structure view, then you double-click their OnEval (or other) event in the properties editor, the IDE will create a method with a name like the above and you can implement there.
If you declare the methods manually, then you also have to attach their events manually, but it's not different than what you end up doing for a button click event (and where you end up with a Button1Click method).
Apart from the implementation code itself, most of the declarations in DWScript can happen at design-time, and are stored in the DFM. The Structure View is convenient for that, but you can also use the property editor only (it was even more convenient in older Delphi versions, when the structure view was sticking to design-time structures, rather than being reused for source structure).
There are some program tools such as WinSpy++ which will allow you to hover over the Handle of any Control/Component and return the Class Name of that Handle. So for example, if I dropped a TMemo on a Delphi Form and compiled the Application, if I used WinSpy++ and hovered over the Application (above the Memo), it will reveal the Class Name of the Editor as TMemo.
Now, suppose I dont want anybody using such a program to determine the Components I am using in my Application, how would I prevent Class Names from showing up in a tool, such as WinSpy++?
I ask because I dont want anyone to easily create clones of any Applications I may create and release, if the Class Names of the Components I am using are discovered it would make their task easier because then they know what to use.
Simply put, how can I hide the Class Names of the VCL I use in my Delphi Application from external viewer tools like WinSpy++.
WinSpy++ can be found here: http://www.catch22.net/software/winspy
To add, I know I can custom derive these components to change the Class Names to my own, but their must be an easier way.
You could override CreateParams and put your own class name into Params.WinClassName. The default behaviour is implemented in TWinControl.CreateParams:
with Params do
...
StrPCopy(WinClassName, ClassName);
Three components, working together:
* CompA, a TComponent descendant, a mastermind component knowing many things and tying things together
* CompB, a TComponent descendant, mines some data from it's CompA and crunches it. Can amongst other things feed CompC with data to present
- Has a published property of type CompA
* CompC, a TComponent descendant, a TFrame descendant drawing surface that can be set at designtime to use a CompB as data provider
- Has a published property of type CompA
- Has a published property of type CompB
I think I remember having read, even though I cannot state where, that Delphi's streaming engine reads all components from the .dfm and builds a dependency graph. This graph is then used to create all components in correct order. For the listed components it should be CompA first (since it uses none of the other ones), then the CompB (it uses CompA and must be created after) and lastly the CompC since it has properties of both the other component types.
This does not happen. CompC is created before CompB. If i rearrange the order in the .dfm file using a text editor it works. The property values are not used in any constructors, only in the Loaded procedures. But truly there must be a way to make it work no matter the order of components in the dfm?
I've been banging my head against the wall for two days straight now, I need somebody to tell me which keyword I forgot or what error in design I have.
I suspect your fault is you're trying to access other objects properties on setters for sibling pointers, forgetting that at dfm loading stage --runtime-- you can't be sure pointers to other components your component depends on are yet valid because it is possible that other component is not yet created. This works this way since Delphi 1.
Because of this, you usually deffer the reading of other component's state (for example) to your overridden Loaded method.
When the streaming system loads a form or data module from its form file, it first constructs the form component by calling its constructor, then reads its property values from the form file. After reading all the property values for all the components, the streaming system calls the Loaded methods of each component in the order the components were created. This gives the components a chance to initialize any data that depends on the values of other components or other parts of itself.
Note: All references to sibling components are resolved by the time Loaded is called. Loaded is the first place that sibling pointers can be used after being streamed in.
Because of this, usually on a setter method for a sibling pointer property you usually perform a check of this type:
procedure TMyComponent.SetDataSource(Value: TDataSource);
begin
FDataSource := Value;
//streaming in stage
if not (csLoading in ComponentState) then
ReadDataSourceProperties;
end;
procedure TMyComponent.Loaded;
begin
ReadDataSourceProperties;
end;
Take a look at the VCL source, you'll find hundreds of examples of this.
If your components are that much dependent on creation order, you are always going to be in trouble relying on the streaming mechanism. Just one addition or removal of a(n other) component on the form/datamodule can throw your order out of whack.
To ensure proper creation order, you'd be better off creating them at run time. Just note that when you create components at run-time the Loaded method will not be called. You will either have to do it yourself or move the code to some init method that you call after you create your components.
You can right click a form/datamodule and select the "Creation order" item. It will allow you to select the creation order of "non visual" components. Visual ones should follow the tab order, but I am not really sure about that.
Update: I was wrong about the tab order, but it looks the visual controls are streamed to the .dfm in Z-order. If the controls are instantiated following the order they are in the .dfm, you can use Edit -> Bring to front/send to back (or the Control menu in the form context menu) to change the z order. As long as the controls do not overlap you should be enough free to change it.
To avoid singletons and global variables I'd like to be able to pass parameters to a TFrame component. However since a TFrame normally is included on form at design time it is only possible to use the default constructor.
The parent form can of course set some properties in the OnCreate callback after the TFrame has been created. However this does not ensure that a property is not forgotten, and the dependencies are not as clear as using a constructor.
A nice way would be if it was possible to register a factory for creating components while the dfm file is being read. Then the required parameters could be passed to the TFrame constructor when created by the factory. Is there a way of accomplishing this?
Or does anyone have a better solution on how to pass parameters to a TFrame?
All components, including descendants of TFrame, need to be able to be constructed using the constructor inherited from TComponent. Otherwise, they can't be used properly at design time. If the restriction of design-time use is acceptable to you, then you could override that constructor and raise an exception. That would prevent the component from being placed on a form at design time. Simply provide some other constructor that requires other parameters.
Because of the design-time requirement, all components need to be able to exist with some or all of their properties still at their default values. That doesn't mean the components have to do useful things while they're in that state, but they do need to be able to stay in that state indefinitely. It should be OK, for example, to place a component on a form, save the form, and close Delphi, with the intention of resuming the form-designing at a later time. The component should allow itself to be saved and restored, even if all its properties haven't been set up for final use yet.
My preferred option is to enforce the component's rules only at run time. Check that all the properties are set to sensible values before you allow them to be used. You can use assertions to enforce the proper use of your components. Consumers of your classes will learn very quickly if they haven't finished setting up your components on their forms.
I would normally add a public, non-virtual "Initialise" or (Initialize to Americans) procedure which requires all parameters to be provided. This will then set the properties.
Make the properties protected or private if possible, so the only way they can be set is from calling Initialise(AFoo, ABar : integer).
Then in TFormXXX.FormCreate or TformXXX.Create, have:
inherited;
Initialise(foo, bar);
could you create/registercomponent your own tFrame component and
place that on the form - it's create could have anything passed to it.
If a factory could provide the parameters that you need, why don't you just override the default constructor for your frame, and ask the factory-class for parameters?
I usually make my own constructor. I don't like to create frames at designtime anyway.
a) a frame can be created dynamically when required and destroyed when not needed
b) give the frame a public property with either the parameter data type or a data structure and pass the values to the form through the property.
Example:
TAddress - a class to hold the usual elements of an address.
TAddressFra - a frame with the visual controls to display the address
populate an instance of TAddress with values
create an instance of TAddressFra
assign the TAddressFra.address property with the TAddress instance
use the procedure setAddress(o_address : TAddress) to assign the values of the TAddress attributes to the corresponding visual components on the TAddressFra