Can I connect a Delphi TEdit (or similar) simply to a published property of a class? - delphi

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.

Related

Hide properties and events in new component

I created in Lazarus a new component based on TPaintBox. Now in Object Inspector I have all Properties and Events which belong to this base component (TPaintBox).
My question is: can I hide chosen Properties and Events for my component?
For example I would like to leave visible only Width and Height properties.
Can you help me?
Once a property/event has been published, it cannot be un-published.
However, it can be hidden from the Object Inspector, at least (it is still accessible to code).
After your design-time code has registered the component with the IDE, it can then:
in Delphi, call UnlistPublishedProperty() from the DesignIntf unit.
in Lazarus, call RegisterPropertyEditor() from the PropEdits unit to register the THiddenPropertyEditor class for the property/event (see Hide Properties (UnlistPublishedProperty) in the Lazarus forum).
Not sure about Lazarus, but in Delphi TPaintBox is a lightweight descendant of TGraphicControl. The majority of its declaration is just publishing properties. I don't know what your component is doing, but it might be easier to derive it directly from TGraphicControl and duplicate the TPaintBox code wherever it actually is needed. Then you can publish only the properties you want. Note that you still have those properties declared published in TControl and TComponent.
No, you can't hide (unpublish) published properties.
In Delphi most objects are based on a parent classes with all the same properties, but mostly hidden.
So while you can't hide exposed properties you can usually achieve what you want by basing your class on the TCustomxxx instead.
Sadly, TPaintbox is an exception. It is descended from TGraphicControl, but that in turn is descended from TControl which already has a number of published properties, including AlignWithMargins, CustomHint and several others, and that in turn is descended from TComponent which has Name and Tag published. To be fair, you need name for sure, and Tag is not a problem I would think.
If you just had to go back to TGraphicControl, that is not too bad. Just one member and a couple of routines to copy. But to go back to TComponent, which is what you would need to do to hide a number of properties is not really viable.

Rule-based properties management

If one looks at hierarchical structure of html and dfm, he could find similarities (nested structure, properties (attributes) and so on). The next step is to ask is there something like css (Cascade style sheets) in Delphi world. So for example rule like this
TPanel TPanel BorderWidth=2
could define that for any TPanel placed in another panel the property BorderWidth should always be 2.
or
TPanel.MyPanel TLabel Font.Style=[fsBold]
for TPanel named MyPanel any label inside should be bolded (similarly to css The particular name (MyPanel) can be equivalent to id attribute of html)
So is there library/unit that was developed for something like this?
I suppose there should be some challenges
implementing this in design-time can pose some compatibility problems with internal form designer of Delphi (Probably this library can be more run-time oriented)
In html world it is mostly about visual so an error could lead just to visual peculiarities, but in Delphi properties sometimes intended to be set explicitly by the programmer's hand and making it subject of mass management could pose security and stability threats.
Nevertheless, I thought that we are too bound to properties manual changing. Even if it's copy-paste, the root of any property change is a dedicated mouse click.

Is there a component to automatically create a config form?

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.

Delphi 2009 creates my components in wrong order

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.

What's the difference between CreateWnd and CreateWindowHandle?

Delphi components have CreateWnd and CreateWindowHandle (and DestroyWnd and DestroyWindowHandle). They're both intended to be overridden by descendants, right? And not intended to be called except by the underlying VCL implementation?
What's the difference between them; when should either of them be overridden?
So far most of the answers here are pretty much on the mark and you would do well to heed their advice. However, there is a little more to this story. To your specific question about when you would override one or the other, I'll try and nutshell things a little bit.
CreateParams();
In general, most of the time all you really need to do is to override CreateParams(). If all you want to do is to subclass (remember Windows style "subclassing?" See Petzold's seminal work on Windows programming) an existing control class and wrap it up in a VCL control, you do this from CreateParams. You can also control what style bits are set and other various parameters. We've made the process of creating a "subclass" very easy. Just call CreateSubClass() from your CreateParams() method. See the core VCL controls for an example such as TCheckBox or TButton.
CreateWnd();
You would override this one if you need to do a little bit more with the window handle once it is created. For instance, if you have a control that is some kind of list, tree, or otherwise requires post-creation configuration, you'd do that here. Call the inherited CreateWnd, and when it returns (you know you have a valid handle if you return from CreateWnd because it will raise an exception if something went awry), just apply your extra magic. A common scenario is to take the data that is cached in an instance TStrings list and actually move it into the underlying window control. The TListBox is a classic example of this.
CreateWindowHandle();
I had to go refresh my memory on this one, but it seems this is one is rarely, if ever, overridden. In the few cases inside VCL itself, it appears that it is used to work around specific Windows version and locale oddities with some controls, such as the TEdit and TMemo. The other more clear-cut case is in TCustomForm itself. In this case it is there to support the old MDI (mutli-document interface) model. In this case MDI children cannot be created using the normal CreateWindowEx() API, you have to send a message to the MDI parent frame to actually create the handle. So the only reason to overide this method is if the actual process of creating the handle is done via a means completely different than the old tried-and-true CreateWindowEx().
I did notice that your question was merely asking about the creation process, but there are corresponding methods that are overridden in some cases for both handle destruction and the "voodoo" that sometimes surrounds handle recreation. But these are other topics that should be covered separately :-).
CreateWnd first calls CreateParams, then calls CreateWindowHandle using the created Params. Generally, you'll override CreateWnd and CreateParams rather than CreateWindowHandle.
I hope this helps!
Who does what:
CreateWnd is the general contractor that creates the fully formed window for a WinControl.
First, it has to set the required attributes for the WindowClass by calling CreateParams and making sure it is correctly registered.
Then it gets the window actually created, by calling CreateWindowHandle which returns the resulting Handle from the OS.
After that, we have a valid window able to process messages, and CreateWnd does the final grooming, adjusting different visual aspects like size, font, etc.
There is also later step done by CreateHandle, after CreateWnd is finished, to help the VCL in managing its windows (identification, parentage,...).
I'm sure that the final answer can only come from the people involved in the creation of the VCL (Allen?), but IMHO the virtual method with the least responsibility / which is lowest in the chain of calls should be overridden. That's why I have always overridden CreateParams() and CreateWindowHandle(). This looks like a good fit since they are both called by CreateWnd(), and both do only one special thing.
In the end it's probably a matter of preference.

Resources