Is there a component to automatically create a config form? - delphi

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.

Related

How to drag and drop nodes from TVirtualStringTree into a VCL control with DragType dtOLE?

Is it possible to drag and drop nodes from TVirtualStringTree into a VCL control when the DragType is set to dtOLE (not dtVCL)
I have a situation where I need to be able to drag nodes from one VT to another VT (dtOLE works fine), but at the same time, while dragging, I need an option to be able to drop the nodes into a TListBox (or other VCL control).
I tried setting DragAcceptFiles() API for the TListBox handle, but it had no effect.
How can it be done?
There is no DragAcceptFiles property on TListBox. If you mean that you are calling the Win32 API DragAcceptFiles() function on the TListBox.Handle window, that would only work if:
you manually subclass the TListBox.WindowProc property to handle the WM_DROPFILES window message.
TVirtualStringTree provides the CF_HDROP format during OLE dragging.
2 is not true, though. CF_HDROP is meant only for dragging filesystem paths, which is not what TVirtualStringTree drags. So using DragAcceptFiles() is out.
TVirtualStringTree (and other TBaseVirtualTree descendants) uses custom data formats during OLE dragging (the interface section of the VirtualTrees.pas unit declares these format IDs, so you do not need to register them manually in your own code):
CF_VIRTUALTREE contains an IStream or HGLOBAL holding a serialized form of the selected tree nodes that are being dragged. The serialization is comprised of a series of data chunks describing each node. You will have to refer to the implementation of the VirtualTree.pas unit to decipher this format (I'm not going to do it here).
CF_VTREFERENCE contains an HGLOBAL holding a TVTReference record (which is also declared in the interface section of the VirtualTrees.pas unit) containing a pointer to the actual TBaseVirtualTree object that is being dragged from and the ID of the process that the tree belongs to.
So, in order for you to be able to drop tree nodes onto the TListBox, you will have to do the following:
write a class that implements the IDropTarget interface (or use a pre-existing implementation, such as from Anders Melander's Drag&Drop suite).
register that class with the TListBox.Handle window using RegisterDragDrop().
in your IDropTarget implementation, you can query the provided IDataObject for the CF_VIRTUALTREE and CF_VTREFERENCE formats. If successful, your Drop() method will have access to the tree node data that is being dragged and can copy it into the TListBox as needed.
Assuming your TListBox exists in the same process as the TVirtualStringTree being dragged from, I would suggest focusing on just CF_VTREFERENCE, since it is a very small and simple format (see the implementation of the TBaseVirtualTree.GetTreeFromDataObject() method), and parsing the CF_VIRTUALTREE data would be overkill (see the implementation of the TBaseVirtualTree.ProcessOLEData() method) when you can just enumerate through the source TVirtualStringTree directly instead.

Custom grid with LiveBindings - EBindCompError 'No list control editor available'

I am designing my own Delphi XE5 custom grid. I'm not interested in VCL, so I am working with FireMonkey only. Since it will have to be data-aware, LiveBindings is a must. The task is complex, so I will try to ask just for very specific problems.
A foreword. I am finding FireMonkey (just like the old VCL) a bit difficult to extend. It does use interfaces extensively, but there is still a lot of code that is not overrideable or that references private members. I find myself having to copy entire classes just to change 5-10 lines of behaviour.
What I have now is (ME.Grid):
TCustomGrid -> TMECustomGrid -> TMEGrid (my custom grid)
TColumn -> TMEColumn -> TMExxxColumn (specific custom column classes)
Txxx -> TMExxxCell (specific custom grid cell classes)
All registered with RegisterFmxClasses. TMEGrid is also registered with RegisterComponents.
I also have (ME.Bind.Grid):
TMELinkGridColumnDescription = Class(TLinkGridColumnDescription)
TMELinkGridToDataSourceControlManager = Class(TInterfacedObject,
ILinkGridToDataSourceControlManager)
TMELinkGridToDataSourceColumnFactory = Class(TLinkGridToDataSourceColumnFactory)
... the latter is registered with RegisterLinkGridToDataSourceColumnFactory
All this with a minimal implementation. I'll add/change behaviour afterwards. My goal now is to drop a TMEGrid on the form, drop some dataset and have the bindings do something with my grid.
What I get (when the application fires up) is EBindCompError 'No list control editor available'.
Since this worked when I had derived TMEGrid from TGrid (which was unacceptable for other reasons), I checked around and I noticed that Bind.Editors references TGrid explicitly (instead of TCustomGrid). So I wrote my own (ME.Bind.Editors):
TMEBindListGridEditor = Class(TBindListEditorCommon,
IBindListVirtualEditor,
IBindGridEditor,
IBindListVirtualEditorScope)
TMEBindGridEditorFactory = Class(TBindEditorFactory)
... the latter is registered with RegisterBindEditorFactory
But this did not seem to change anything. I still get the same error message.
Any ideas?
Keep in mind that all the components, factories, etc... are in their own package (MEComps). The project just has the one form with a grid and some other stuff (a navigator, some buttons).
Edit: I think I have found a clue. I checked my source from within the test project and I found that the
RegisterBindEditorFactory([TMEBindGridEditorFactory]);
code line is not compiled into the project (I can't put a break point there). Obviously it is not being pulled into the project, which is certainly not a good sign. So I checked where the counterpart unit is being used (FMX.Bind.Editors) and I found this very interesting BindCompFMXReg unit, of which I have no equivalent. I'll study this to see if I can get somewhere.
first I'm sorry, I am Brazilian and I can not speak English, so I used Google translate :)- , but would like to help you ...
I had the same problem, I created a grid that constructs the query and columns dynamically, but to activate this grid, the error was generated EBindCompError.
The reason this happens is that LiveBindigs structure needs an editor of the same type that is used in design time.
I looked in the Delphi code and found a simple solution for my case which can also be for your case.
I imported the library Fmx.Bind.Editors and declared a variable of type TBindListGridEditor and just created in the Create method of my Grid, below example:
MyUnit unit;
interface
uses
   Fmx.Bind.Editors ...
type
   TMyGrid = class (TGrid)
   private
    FBindingEditor: TBindListGridEditor;
   public
     constructor Create (AOwner: TComponent); override;
   end;
Implementation
constructor TFBGrid.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
FBindingEditor: = TBindListGridEditor.Create (Self);
end;
end.
I have helped you.

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

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.

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.

Delphi IDE treating TFrame as if it were a Form

Every once in a while when I am tweaking my TFrame classes (adding properties, methods, etc), the IDE gets confused and acts as if it thinks the frame is a form, complete with header/caption, borders, etc. Yet, clearly the class is declared as a TFrame descendent. Any ideas as to what causes this, how to prevent, and how to fix?
I'm using Delphi 2007 Pro. Also note (if it matters), the TFrame descendents are typically registered with the IDE (i.e. on the palette) via a design-time package.
Later: Additional "specifics": The frame that I'm having this problem with at the moment is, visually, a VERY basic TFrame (only change from brand new TFrame is size, and background color).
Here's its class declaration:
TBasePanel = class(TFrame)
private
FPanelManager: TPanelManager;
procedure SetPanelManager(const Value: TPanelManager);
protected
procedure Connect; virtual; abstract;
procedure Disconnect; virtual; abstract;
procedure Refresh; virtual;
procedure Requery; virtual; abstract;
published
property PanelManager: TPanelManager read FPanelManager write
SetPanelManager;
This frame is used as a base class for a number of others. I am usually editing it directly from the BPL project it belongs to (because all of these frames install to the palette), rather than as part of an EXE project, with related Forms open etc.
Also, "Embedded designer" is checked in Tools -> Options.
I am saving all DFM files as text rather than binary as well (if that matters at all).
I have encountered the same problem. The following steps solved the problem for us, it might also work for you:
in the IDE: close all forms that use the frame
open the frame, view as text (*.dfm)
the dfm probably begins with object MyFrame: TMyFrameClass
change this to inherited MyFrame: TMyFrameClass
I don't know what caused the problem.
Perhaps you had unchecked the 'Embedded designer' check box? (Tools | Options | Environment Options | VCL Designer). Then, indeed, your frame is shown at design time as a form (with caption, border etc.). Also a concrete code of your problematic TFrame descendant or more details about your case would help.
As far as I know, you have to have both the form and the frame open in the editor when you edit the frame. Else there can be update problems. Although I haven't seen this one.
But I gave up on frames a long time ago because I did not find them very reliable.
Right now I only use them for prototyping, creating a custom component (derived of a panel with the apropriate controls on it).
You may have to register custom module to the IDE
But your additional properties won't work well unless they are in ancestor class.
I have encounter a lot of problems with TFrame and finally came to such workaround that solves all my problems: I create and design frames visually, but use them only by hand-coding.
As a side effect my applications became smaller, because of less dfm-s.

Resources