When I iterate of the controls on my form, I see those which I placed there at design time or run time. They are all of type TEdit, Tmemo, TComboBox, etc ...
However, there are always exactly eight which I do not recognize. I can skip over them, since they are not of a type which interests me, but I am curios.
I am guessing system controls like min/max/close. Their Name property is empty. Is there any way I can determine what type they are (without explicitly testing for every standard component derived from TWinControl) ?
I am curious - but not yellow ;-)
for i := 0 to Form1.ControlCount -1 do ...
Answer: use ClassName to find out. And i got TGrabHandle for all of them.
Thanks for all the help. +! all round
Use their ClassName to get an idea of wht they are when the Name property is empty.
TObject has the property ClassName; Try to access and interrogate this property for this components.
Paste the code block (for/while) that you are using for interrogate the form, for search components.
Regards
Related
I've had this problem for years but maybe it is now possible to easilty solve it. I need to lay out a panel with several TEdit controls, each should show, and allow editing of, a published property of a class. Traditionally I would use TEdit (or a numeric derivative from the Raize or Developer Express libraries) and 'wire up' the OnKeyPress and OnExit events, convert between the edit text and the property type etc etc. All as per Delphi 1 (whose big birthday is soon!).
These days we have RTTI and Live Bindings, so ideally I'd like a way of telling a TEdit (or another similar control) about a single published property and the necessary 2-way link would then be established without all the wiring up and conversions. An object inspector does this job of course, but I'd like a more formal custom layout using labelled edit controls. It would be fine to simply cope with integer, float and string, and something like a TDBEdit where the field name was my property name would be great.
I've taken a look at the 'Bind Visually' designer (I have XE3) but I'm on to uncertain ground. Can anyone suggest a means of doing this? Thanks.
The comments above by Ken White and Sir Rufo are good pointers to the use of Live Bindings for wiring up components between each other, but I need to wire up controls to my own object and which is created at runtime. Further digging led me to this excellent article which pretty much does what I want. Jarrod's TBoundObject is intended to be the ancestor for your own objects, but by including an FObject field passed in the constructor and replacing his use of 'Self' by FObject, you can instantiate a standalone 'TObjectBinder' that easily connects various standard controls to published properties.
Base from the anwser How to eliminate variables... i get and accepted it works great when i have all this components and make the actions like a button1.click from the main form...
But i use to make the actions from units... so
When i click a button i great a procedure DoMaths(Sender: TObject);
procedure Tform1.DoMaths(Sender: TObject);
begin
if TButton1(Sender).hint := 'Make the standard Package' then
do_Maths_standard_package;
end;
the do_Maths_standard_package is in unit ComplexMaths.
is the procedure do_Maths_standard_package form unit ComplexMaths it calls some components form Form1... like Form1.label1 etc...
So when i call the RegisterClass(TLabel) and erase the Tlabel from the type it gives an error that it cant find the Label1...
Please can someone help me so not to do the hole program from the start...
Thank you..
You might be able to reference your components like this:
TLabel(Form1.FindComponent('Label1')).Caption := '...';
TCheckBox(Form1.FindComponent('CheckBox12')).Checked := False;
But it's really a pain...
I think you have two options.
1) you can assign each component a unique numeric ID.
And save it into .Tag property.
Just like u use to generate and bind IDs in .HelpContext properties.
Then to get the control by number you would enumerate Form.Controls and get the one with proper Tag value.
The problem would be to have two separate ID lists, in PAS files and DFM files, in sync. Mistyping would be hard to notice. Especially since you have no constants in DFM but only "magic numbers".
2) Set .Name property and use iMan Biglari's recipe - FindComponent by name.
The question is if you can have .Name but not variable. Since no one answers - just try and see.
To my experience - with Delphi 5, hopefully D7 is mostly the same - you can just delete the variable.
If you made wrong declaration of variable, then Delphi editor would notice and ask to correct it.
If you have variable without DFM object, Delphi would notice it and ask to remove it.
But if there is DFM object without corresponding variable, then Delphi editor is oblivious. Maybe it thinks that the object is inherited or whatever.
But if you did not declare it at all it does not mind.
However, since you deleted Names, it looks like it is not possible to you for some reason.
In both cases you would have to cache value if some procedure makes a lot of accesses to some control. And maybe even across the procedures. In effect yu would manually restore those variables at least for most used controls.
I have a settings class in my system, where I store configuration values for important settings. It looks like the code below, but with many more attributes of different types.
Now I need to implement a form with which I can adjust and configure these settings at runtime. Its very cumbersome to implement the fields for each attibute and guarantee that all values are mapped without error.
Now my question: is a vcl component that could automatically create an interface to solve that. Eg. offer a tree-like or listview-like interface with the names of the attributes and fields to edit the values (like the property pane in the IDE, with printer settings, see screenshot below). That would be a great thing. No?
How do you deal with configuration forms like that?
Thanks for your input!
TGoldmannSettings = class
private
FInitialSymbolSize : Integer;
FPenWidth : Single;
FCanvasColor : TColor;
FShowLiveCoordinates : Boolean;
FFont1 : TFont;
FMsmPointSymbol : TAvailableSymbols; // own type
...
public
constructor Create;
destructor Destroy; override;
property SymbolSize : Integer read FInitialSymbolSize write FInitialSymbolSize;
property Font1: TFont read FFont1 write FFont1;
...
published
property PenWidth: Single read FPenWidth write FPenWidth;
property CanvasColor: TColor read FCanvasColor write FCanvasColor;
property ShowLiveCoordinates: Boolean read FShowLiveCoordinates write FShowLiveCoordinates;
...
end;
You sometimes find something I mean in printer setting dialogs:
The TJVInspector component from the Delphi Jedi JVCL project creates a property editor very similar to what you are looking for. They have an advanced example that works on an INI file.
JVCL Site: http://jvcl.delphi-jedi.org/
Nice example: http://www.neugls.info/?tag=tjvinspector
The JVCL / JCL package is huge but has a ton of useful components and functionality.
I have never yet created an automatic-configuration form generator similar to the one in the delphi project options, but I have seen this done in several projects that I work on, and seen the source code, and it works very much like this:
I would have a base type of frame called TConfigFrameBase and it would contain some properties like this: Caption (user displayed name of property), Hint (some help), and Name (config property), and Section (page this property is shown on).
Specialized inherited frames would be used for boolean properties, string properties, etc. Your domain (your app) will have its own custom types. Dates? Lists of apothecary locations in Denmark? Only you know for sure the complete set of UI configuration property types you need, and that's why I haven't seen a component that makes this automatic or just a component. The boolean frame would contain a Label control and a checkbox and would have a default height around 30 pixels. A frame that let me move a list of options on side A to options on side B (columns visible within a particular grid, for instance) might be as high as 300 pixels. By vertically stacking these frames, in a scrollbox, you don't have to do much thought about layout. Everything will be usable when these frames are used to populate a listbox.
A tree view on the left that lets you pick a section. In the on-click in the tree view, the right side pane is built by iterating through my internal list of config-frames that are registered in a list or dictionary, and filtered by the Section they belong to.
I wouldn't use a JVCL Property Inspector as my configuration control, but it might work for you. I also don't think you're going to get everything you need out of VirtualTreeView, but your mileage might vary. You can write your own custom Editor controls, and if you like writing in-place-editor controls, you might find VirtualTreeView perfect.
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.
Working with my ongoing TFrames-based component set project, I'm coming across various instances where I am wanting to replace one of the TFrame's components (usually non-visual) at runtime with one that is generated dynamically at runtime.
I think I've probably found the answer to my immediate problem here, but in my own digging around and experimenting prior to finding that, it's become clear I've got quite a bit to learn about how Delphi handles object references, particularly with respect to forms/frames, and (in general) class properties which are object references rather than non-pointer values.
A specific example of one experiment is here:
(On a form with three TButtons)
procedure TForm1.Button3Click(Sender: TObject);
var
MyButton : TButton;
begin
MyButton := TButton.Create(Self);
MyButton.Caption := 'New Button';
MyButton.Parent := Form1;
Form1.Button2 := MyButton;
Form1.Repaint;
ShowMessage('Button2 caption = ' + Form1.Button2.Caption);
end;
Doesn't replace Button2 with the created button, but does show both on the form. The ShowMessage results indicates Button2's caption still = "Button2"
I find myself asking questions like, "Is this 'non-replacement' unique to forms, or would that be true for other classes as well?" etc. In short, I've discovered yet another sinkhole of my own ignorance. ;-) I find in working with instances/object references/derefencing/class definitions/class properties etc, that oftentimes things behave exactly as they expect them to, but other times, not at all, and not even close.
It's clear I need to study up on this area. Rather than post stupid question after stupid question revolving around this subject, I thought I'd ask this instead:
What is a really good reference or tutorial for getting a better grasp on the subtle distinctions re: how Delphi handles such things?
Thanks in advance for all your help!
Those fields on the forms are there purely for your convenience when writing code. You can delete them from the .pas file and they'd still show up. The form's layout is defined in the DFM, and the form object holds an internal list of references to the controls placed on it, just like any other visual control.
I don't know about tutorials on the subject, but I do know how you can replace a button. You've got it mostly right, but you also have to free Form1.Button2 before you overwrite the reference. That will cause the button to remove itself from the form's control list as part of its destruction process. Or, if you want to save the button somewhere instead of destroying it, call Form1.RemoveControl(Button2); instead.