Delphi: Managing multiple instances of TFrames - delphi

In this article there is an explanation of how to use TFrames as an alternative to TTabSheets in a Delphi PageControl.
I've been pondering a similar exercise, which the above doesn't seem to provide the solution for, and for which my solution seems to be overly complicated.
The requirement is for a tabbed interface, where each tab can be one of a number of different designs. Each design is implemented as a TFrame.
Due to the nature of the application being designed, we may have multiple copies of any frame open at any particular moment (with the content of each tab differing, but not the design) so that the user can compare the details of 2/3 different items at the same time.
For example in one session we may have 3 tabs open, all of Frame design A.
On another occasion we may have 3 tabs open each of Frame A, B and C.
The design needs to be flexible enough that we can add Frames to the design on request.
At the moment the solution that I have is to have separate TLists managing each type of Frame that we have open, with perhaps a master TList to keep track of the tabs that are open. As I said, over complicated.
Does anyone have a suggestion of how this could be handled more simply?

I'd probably leave out the master TList of frame instances.
If you need them you'll be able to get them by interrogating the TPageControl directly or through each of your individual TLists for each frame type.
Apart from that your approach sounds reasonable.

I have an application that manages frames on a single panel with my own menu control to control which frame is visible. When I need to make a frame visible I simple set visible:=false for every frame on the panel except for the one that I want. I am in control of what goes onto the panel so I know that at the very least each control is a Tframe and I can get to each frame by iterating over the Panel's Controls property. I then use interfaces to communicate between my main form and my frames.
Now if you want to use a standard windows tabbed interface you could still use the page control as you have suggested, you know that each TtabSheet has a single Tframe on it and you can check it's type and work with it as required. I don't see why you'd need a Tlist because if you really need to get at the "list of Tframes" you could build it dyamically anyway by iterating over the TtabSheets in the page control.
An alternative which would work similarly to my first approach, but gets you nice Windows tabs, would be to use a TtabControl instead of a TpageControl. With the TtabControl you basically just get a Tstrings instance (in the Tabs property) which represents all of the tabs. Since it's a Tstrings you can associate an object (ie your Tframe) with each item and hence each tab. When you click a tab you hide everything and show the correct Tframe. You also have your list because it's attached to the TtabControl via the Tabs property. You just have to handle the visibility of the frames yourself.

We use a TPageControl and create runtime a TTabSheet descendant, which has a new property for our own TFrame (we do not need to scan through .Controls or .Components to search our frame each time).

Related

How to determine the Z order of multiple visible forms in Delphi?

Using Delphi 7. There are, among other components, 4 non-modal visible forms over a TPanel (the TPanel is the parent of the forms). I use Show, Hide, BringToFront, SendToBack on the various forms and it works fine.
The problem is that at any point in time I would like to programmatically determine the Z order of the forms. For example, Form2 is on top, behind that is Form3, then Form1, and last is Form4.
Or put it in a slightly different way: given any two non-modal, visible forms, e.g. Form2 and Form3, how to determine programmatically which one is above the other?
I tried Screen.Forms, but it doesn't work -- it always gives me the forms in the same order, regardless of the actual visible Z-order of the forms.
I read about the Windows functions GetTopWindow and GetNextWindow but before I try to use that, I'd like to know if there is a simpler way. Especially if I just have two forms with the same parent, is there a simple way to know which of them is above the other?
Thanks for any suggestions!
Use GetWindow passing GW_HWNDNEXT and GW_HWNDPREV to traverse the Z-order.
You say that you don't want to use this method and look for something simpler. There is nothing simpler.

Delphi - TVirtualtreeview encapsulation

I need a very fast treeview able to list and scroll hundreds of thousands of items also with nesting of subitems etc. The standard Windows tree view control (wrapped with Delphi's TTreeView) is not up to the task. It's too slow.
Now I have checked TVirtualTreeView which is as fast as I need, but there is a problem, it does not work at all like the standard treeview but in a completely different way.
I am wondering if some expert using this component can tell me whether it is feasible to encapsulate it in a new component so that it will have properties and methods of a normal treeview but keeping the speed advantage?
If my question is not clear, I will try to elaborate it further.
My solutions for you is:
Add a new frame to your project
Drop a VirtualTreeView on it and align it as alClient
Add methods and properties in order to make the frame to mimic the TTreeView interface
Implement those methods and properties to deal with the internal TVirtualTreeView
Implement TVirtualTreeView events to mimic the TListView behavior
Replace your current TTreeView by the new frame
I guess this will get you close to what you want, balancing complexity and functionality.

hide UI components at RUN TIME , design issue

on a form I need a first set of UI controls at process start, later I only need a second set of components. I put the set for process set #1 on a first panel , after finish process step #1 I start to hide panel #1, is ther any better way from UI design guide lines ?
I would use Frames instead of Panels. You can design Frames at design-time just like Forms, and then create instances of your Frames at run-time as needed. So create your first Frame (you can even place it on the Form at design-time) and then at run-time, when ready you can destroy the first Frame instance and create an instance of the second Frame.
Using panels like you describe is a very common and very respectable way to proceed. Nothing wrong with doing it that way.
Another often used idiom is a page control with hidden tabs. So, make a page control. Add two tab sheets. Set TabVisible to False for both tab sheets. Then at run time set the ActivePage property of the page control to specify which page is shown to the user.
The page control approach is really rather similar to using panels. It's perhaps marginally easier to work with at design time, but much comes down to personal preference.

Delphi, frames vs forms. What for multi-document interface?

yesterday I've started discussion on "MDI vs tabbed interface". I've asked whether should I continue developing my app as MDI-based, or should I embed the child forms into tab sheets.
Someone pointed that I should use TFrames instead... My question is: why?
What are pros of using TFrames when embedding the form over TFrame? So far I don't know any, switching would only require me to rewrite some parts of code...
(I'm not going to use embedding at design time anyway!)
Thanks in advance
Answering the comment to provide a reason why to use frames:
I would consider frames to be building blocks of the GUI, with design time combination of existing components to more advanced components. Before Delphi 5 one would have used a TCustomPanel descendant with child controls and registered this as the new component, ready to be dropped onto a form. Frames allow for the same thing with less hassle.
They allow you to concentrate on developing exactly the functionality you need, and nothing more. Done right you can then embed them into tab control sheets, into modal or modeless dialogs, into MDI child frames and into standard frames. You can even add several of them into one form - something one would probably not do with embedded forms. The point is that for maximum reusability a layered approach is often necessary, and frames help with that.
A frame is fit for embedding from the go. A form has to be adapted to not show a caption bar and border, normally one would override the CreateParams() and adjust the window style accordingly. There are a lot more form properties in the inspector that just don't make sense for an embedded form. IMHO one should use the most basic and generic entity that suffices. A form just is much more than a control container for embedding.
OTOH I don't know of any disadvantage of embedding a frame that embedding a form wouldn't have.
Edit:
There's a comment regarding events like OnCreate or OnShow that frames don't have. Actually, I'd consider that another advantage of frames, as event handlers don't have parameters, so a lot of stuff gets hard-coded in forms, necessarily.
Consider the case of per-user settings: in OnCreate there's not much information available, so one invariably ends up using a constant or the name of the form for the INI file section, making it very hard or even impossible to reuse the form or to create several instances of it. With frames on the other hand a method LoadSettings is the obvious way to do it, and it can carry the necessary parameters. That way control is returned to where it belongs, to the container of the embedded frame / form. Reusability is only possible if the behaviour can be adjusted from the outside.
For contained objects that are not components and need to be lifetime-managed, there are for example AfterConstruction and BeforeDestruction.
Maybe you will find some answers in this thread: gui-design-multiple-forms-vs-simulated-mdi-tabs-vs-pagecontrol
Frame use the fastest load and without delay when creating the frame.
But the frame should be has a parent to embedded it. Disadvantage with no onCreate or onShow event has been triggered. but you can call with message for trigger onShow event like this one :
put on private section of frame:
procedure CMShowingChanged(var M: TMessage); message CM_SHOWINGCHANGED;
and then create the code like this :
procedure TFrame1.CMShowingChanged(var M: TMessage);
begin
inherited;
if Showing then
begin
// .... put your code for onShowing is triggered
end
else
begin
// .... put your code for onHiding is triggered
end;
end;
Hope can helping you to consider embedded frame for GUI.
You may consider combined with PageControl to control your frame opening.
Manz
I had same decision few years ago for one of our applications, we wanted to make it looks embedded forms, first I used the Frames and I wrote a class to manage it.
Later I found TLMDDisplayForm component from LMDTools, which making embedding forms inside it very easy task, it reduced the code used and we have more features.
one of main goals that we changed from frames to Forms was missing some events of TForm like: OnCreate, OnShow, OnActive which we use for some tasks in our applications, beside missing some properties such as: ActiveControl and other things I don't remember.
If you would like to go with Forms, I suggest you to use LMDTools which make the task easier for you, beside the basic version is free :-)
For dynamically inserted forms/frames I personally prefer to use embedded forms over frames. Several versions back when one would edit a frame which was set to alClient, the frame would resize between edits and any controls which were aligned specific to the right of the frame would change position. When using embedded forms this didn't happen so I made the switch. I believe this issue is now fixed with later versions of Delphi.
I strongly agree with the points Mghie made earlier regarding the inability to pass information to the embedded form through notification events. To solve this I generally implement a series of interfaces in each embedded form for communication. This really simplifies the code, and allows for more generic implementations where you have a single "container" that will be dealing with many different types of embedded forms/frames. A few examples of this are available on my blog as part of the wizard framework I designed.
I think both should be used. For example, I have a "standard" frame that has a dbnavigator, dbgrid and datasource components which is very handy for the typical data browsing. Instead of inserting such components every time, I insert a frame that also has the ability to export its data (with JVCL :D) to several formats...but I know what I want to display at design time, so I suggest a very easy rule: if it is known at design time, use frames, otherwise use embedded forms.
However, keep in mind that forms were not designed to be embedded. Using them like so, its unnatural (as a vampire states when she buries her 80 old year daughter and she looked like 30 :D). The embedded form knows little about the one that owns it and can also (logically) assume that is not embedded.
Complementing that, a frame is a component, and so, when embedded (owned by) in a form, the form knows about it and the frame knows about the form (can use its methods and properties. An embedded one can also do that but requires extra coding)
Perhaps Embarcadero could give us a hand by creating a TEmbeddableForm or an interface for such purposes
Regards,
Alvaro Castiello
Frames are good when you want to repeat a "sub-form" multiple times in a form. I'd not use them for tabbed interfacing, as the embedded form is a better solution for MDI/Tabbed interface use.

Design-time drag and drop in Delphi?

Before Delphi 2006 (I think) introduced the TFlowPanel and TGridPanel, I did a control that was similar in concept. It still does a couple of things those controls do not do, and when upgrading my code to Delphi 2009, I decided to add a couple of enhancements to that as well.
Right now, the order of the child controls is determined by their creation order. The FlowPanel and GridPanel show a better way with ControlIndex and other filtered properties, but I was wondering if there is a way to handle drag and drop reordering in design-time? As far as I can tell, dragging an edit control and dropping it onto my panel doesn't call anything that I can access at design-time.
I was half-fantasising about a way to either detect the drop operation directly, or to perhaps detect when a control is moved so I can determine where it should go.
Any ideas?
Update:
OK, got it working. The container control was already overriding AlignControls to manage the placement of the controls. When you drag the nested control and drop it, AlignControls is again called. I then compared the new coordinates of the control with the other controls in the list and moved it to the appropriate position.
There were a couple of problems that I had to work through (mostly related to the many calls to AlignControls) but the basic concept is simple enough. Thanks to all the commenters for all the help.
You can't drag a control that's already on the form and drop it onto your panel. Dragging is only for moving a control, not for changing its parent. To change the parent, cut and paste.
If the control is already on your panel, and you want to move it to another position on your panel, then the panel can control the layout by overriding the TWinControl.AlignControls method. When a control is moved, its SetBounds method is called, and among the things tha happens is that it calls AlignControl(Self) on its parent window. That calls AlignControls. Look in Controls.pas, and you'll see that that's a complicated method, but it's what is responsible for the layout of the children on a control, and that's exactly what you're planning to change.
Perhaps some of these suggestions might help.
You can re-parent a control in the designer without having to do cut-and-paste. View the structure pane, and simply drag the visual control to the node of another parent in the structure pane. If you have things in a flowpanel, drag everything out of the flow panel and drag them back in the order that you want them to be.
(You can re-parent ANY visual control this way, without changing anything other than its parent. I highly recommend doing it this way.)
You can view the form as text, and move the declaration order around in there -- but obviously you'll need to be careful when editing the "resource" file directly.
You can set tab order in the designer, so you could make a different control based on tab order that works as you want. You can right click on the form and change the creation order of the non-visual controls, but that doesn't work with visual controls.
Have you tried to write an "OnDragDrop" event for your grid component, where you check if your component is in design mode?
I haven't written such a component yet, but I don't see why the event shouldn't trigger.

Resources