Please help me with my question.
I have TreeView and Frames, how can I shift them if I click on an item of TreeView?
Is it better to use PageControl (PageControl1.Pages[i].TabVisible := false;) instead of Frames or Frames fit better?
Thank you very much!
To answer your first question "how to ... using a TreeView?" : Implement the OnChange event of the TreeView. The node parameter refers to the newly selected item.
About your second question "Should I use Frames or a PageControl?" : Well, one does not exclude the other and you perfectly can use both. Indeed, I advice to do so when you use the contents of such a TabPage multiple times. In those cases, place the Frame with Align = alClient on your TabPage.
Frames are usefull to design an arbitrary reusable container. For instance: you could set the same FrameType on every Page of the PageControl, assuming they all look the same but each working with different data.
Another possible minor advantage of using frames is not to get confused about all the controls on the TabPages.
But if every TabPage is unique in terms of visual style or control layout, then it's perfectly ok to not use frames and design the pages on the PageControl directly.
And about the shifting part: I don't exactly understand what you want to accomplish by setting the visibility of a tab, but shifting to another page (depending entirely on your implementation) based on the node could be as simple as:
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
PageControl1.ActivePageIndex := Node.Index;
end;
Related
I have a TStackPanel which I want to add a number of frames into at runtime. The number may vary each time the form is opened.
There seems to be limited information about TStackPanel around, and the examples I can find are in languages other than Delphi.
I have a loop that creates the frame, gives it a unique name and then adds it to the TStackPanel:
for i := 0 to 10 do
begin
mfSubFrame[i] := TMyFrame.Create(Application);
mfSubFrame.Name := name_array[i];
StackPanel1.InsertComponent(mfSubFrame[i]);
end;
This does not put anything in the stack panel. If I change the SP line to:
StackPanel1.InsertControl(mfSubFrame[i]);
then I do get a frame in the SP. It is the last one of the loop as I can tell by the name, the others may be hidden behind it but I can't tell. They are certainly not stacked horizontally like they should.
I have tried various other things like setting the parent of the frames to be the SP, and had a look at things like:
StackPanel1.Components.InsertComponent(mfSubFrame[i]);
and other sub-methods, but had no luck so far.
I suspect it may require a combination of statements, like add a control item as well as the actual component, but as I am working on the basis of trial and error it could be a long time before I stumble on the right combination.
I have never used the TStackPanel before, but it seems like you can add controls to it exactly the same way you add controls to any other windowed control: just create the control and assign its Parent.
For example,
for var i := 1 to 10 do
begin
var Memo := TMemo.Create(Self);
Memo.Parent := StackPanel1;
end;
will add ten memo controls (all owned by Self) to StackPanel1. There is no need to name the controls; referring to components by string name at runtime is an antipattern. (So is using FindComponent.)
InsertComponent() changes a component's Owner, which has no effect on visual display. You are creating each frame with Application as its Owner, and then changing its Owner to StackPanel1. You should assign the desired Owner when calling the component's constructor.
InsertControl() changes a control's Parent, which does affect visual display. You are creating each frame without a Parent, and then changing its Parent to be StackPanel1. You should be using the actual Parent property, not calling InsertControl() directly.
That being said, TStackPanel has a ControlCollection property that you are not doing anything with. That collection manages the actual stacking.
If needed 1, for each frame, try calling StackPanel1.ControlCollection.Add(), and then assigning the frame to the TStackPanelCollectionItem.Control property.
1: I don't have the source code for TStackPanel to look at, but I suspect TStackPanel probably handles this automatically for UI controls dropped onto it at design-time, but you might need to perform it manually for controls that you create dynamically at runtime. I'm not sure.
I have 3 grouped Tool Buttons (a Tool Bar). One of them is always down. And I have 3 frames. What is the easiest and right way to change the frames switching among the buttons?
Thanks!
The right way is moot at best. One of the easiest ways of many can be to set unique Tags for the grouped buttons, f.i. 0, 1, 2, then set all of the three button's 'OnClick' to the same handler and show one of your frames according to the tag of the clicked button and hide the others:
procedure TForm1.ToolButton1Click(Sender: TObject);
begin
Frame1.Hide; // will return immediately if already hidden
Frame2.Hide;
Frame3.Hide;
case TToolButton(Sender).Tag of
0: Frame1.Show;
1: Frame2.Show;
2: Frame3.Show;
end;
end;
This is assuming you've already put the frames on your form at design time. Don't forget setting the Grouped property of the buttons and their Styles to 'tbsCheck'.
If the frames are all in the same position on the screen, then doing it the way the Sertac suggests will make it really cumbersome to see in the IDE what they look like on their owner form
I suggest you put the frames in a page control or tab control.
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.
I was wondering why Delphi (2007) provides three widgets that seem to do the same thing, and what the advantages/disadvantages are for each.
On the same topic, if I want to display different sets of controls, why should I favor eg. PageControl + TabSheets + Frames, instead of just displaying different frames directly on the parent form?
Thank you.
From the helpfile on TTabSet:
Tab set controls are commonly used to
display tabbed pages within a dialog
box. TTabSet is provided for backward
compatibility. Use TTabControl
component in 32-bit Windows
applications.
So the real question is, what's the difference between TTabControl and TPageControl? The difference is that TTabControl only has one "page", whereas TPageControl has one page for each tab. This makes them useful in different situations.
TPageControl is useful for dialogs where you want to fit more UI on the screen than you have screen space to fit it in. Organize your UI into categories and put each category on one page. You see this pattern a lot in Options dialogs, for example.
TTabControl, on the other hand, works well for working on an array/list of objects. Create a UI to display and edit the properties of a single object, and place it on a TTabControl, then create one tab for each object and set up the event handlers so it'll load a new object from the array into the controls whenever you change tabs.
As for the frames question, the main reason to use a TPageControl in conjunction with frames is because it provides a prebuilt way to decide which frame to display. That way you don't have to reinvent a mechanism for it.
One method that I have used with great success is to use frames with a TPageControl and late bind my frames to the tPageControl the first time the page is selected. This keeps the form load time down, by not creating frames which are never viewed, but yet allows the flexibility of being created, the state is maintained when changing between tabs. Recently I switched to using forms and embedding them instead of frames...but the concept is the same.
The same can be done using a single "mount point" on a TTabControl and switching it out as the tab is changed, but then the issue of how to deal with the tab state as tabs are switched back too comes up.
[EDIT] The question comes up how do I handle communication between the frame and the parent form. This actually is very easy to do using interfaces. Just create a new unit that will be shared by the form AND the frame and add two interfaces:
type
IFormInterface = interface
[guid]
procedure FormProc;
end;
IFrameInterface = interface
[guid]
procedure SetFormController(Intf:IFormInterface);
end;
Have the form implement the IFormInterface, and the frame implement the IFrameInterface. When you click on a tab and show a frame, then run code like the following:
var
FrameIntf : IFrameInterface;
begin
if Supports(FrameObj,IFrameINterface,FrameIntf) then
FrameIntf.SetFormController(Self);
end;
your frame implementation of the SetFormController method would then hold onto the reference passed, which would allow it to call upwards into the form.
procedure TFrame1.SetFormController(Intf:IFormInterface);
begin
fFormController := Intf;
end;
Procedure TFrame1.Destroy; override;
begin
fFormController := nil; // release the reference
inherited;
end;
Procedure TFrame1.Button1Click(Sender:tObject);
begin
if fFormController <> nil then
fFormController.FormProc
else
Raise Exception.Create('Form controller not set');
end;
I have an application that uses a frame extensively and needs to hide/show certain buttons depending on which form is active at the time. In order to keep the buttons neat and organized appropriately, I have put them on panels and show or hide the panels as needed for each form. My problem is when each form is initially created, the panels on the frame are out of order even though I am explicitly telling them which order to put themselves into. After I hide and re-show the form, the panels are in the correct order. Any suggestions on how to keep them in the proper order from the very beginning?
Instead of giving the panels explicit positions, try giving them alignments. They tend to stick better than way, and they do a better job of resizing if you resize the form.
You can also try using a stackpanel (or was it flowpanel?) as parent for the panels. Then you will have a order instead of a position to manipulate.
Maybe you can have a look at the DevExpress LAyoutControl? It helps us creating interfaces that always look good, no matter if we show or hide certain groups / panels. It even allows for run-time customization of the interface, if you want that!
You may try to organize them by coordinates i.e.: setting Top and Left. Unless your panels are aligned, this will always work (but it takes bit lot of work).
I had this problem and I found that the solution was to do this in FormCreate (or in a CMShowingChanged method of your frame):
MyPanel1.Align := alNone;
MyPanel2.Align := alNone;
MyPanel1.Align := alBottom;
MyPanel2.Align := alBottom;
Restore in the order that you need - this seemed to sort out the order visually.