Fluid Form Layout in Delphi - delphi

We have developed a software. In this software we are show and hiding a few controls on various input screens depending on various situations.
When we hid a control what happens is that the space occupied by that control is left as it is and layout looks very bad at times esp. in screens that have larger numbers of controls. Our client does not like this and has asked us to do something about this.
My question:
Is there some way by which we can create Fluid Layouts so that when a control is hidden the rest of the controls automatically adjusts themselves to fill the empty space left by the control hidden and when the control is show they should automatically make way for the control and adjust themselves accordingly.
I know we can achieve this by coding but that will require a lot of code in each screen for adjusting the layout. I am looking something which will reduce coding in each screen as there are 80+ screens.
Please suggest some way which is less error pron and can get rid of unnecessary coding in each input screen.

I think your best option is to use a component that handles the layout of your vcl controls on your form in runtime (depending on the conditions that you define). I recommend you try the Devexpress ExpressLayout Control
you can find two great demo videos here
ExpressLayout Control - How to Customize Layout Views
ExpressLayout Control - Create and Customize a Simple Layout
(source: devexpress.com)
You can check these features
Auto-Management - Control groups and individual control elements are automatically managed by the Layout Control. You never worry about pixel-by-pixel positioning.
Form auto-sizing - The form can be automatically resized to fit its contents best.
Bye.

Now, I'm not sure how complex layout you have, but I guess you can use TFlowPanel and/or TGridPanel for this. Flowpanel has a nice handling of components that change visiblity. I'm not sure how well gridpanel handles the same...

What kind of controls are you dynamically hiding, and what do you mean with auto fill space?
I do not know if it is as this simple: place controls on panels, and use align alTop/alClient/alBottom. When you hide a panel, all other panels will move automatically up.
One problem though: if you want to show a panel again, the order of panels can sometimes be screwed up... Can be fixed by manually setting .Top property, or "hide" by setting .Height := 1;

What I would do with a complex layout is actually split it up into several tabs. This has two advantages. It simplifies the form layout, and allows you to show and hide whole tabs depending on choices made in other tabs.

Raize Components have a TRzFlowPanel UI component. Does exactly what you're after.
Use TRzFlowPanel to put an empty flow panel on a form. The major difference between a traditional panel and a flow panel is the way in which controls are placed. With a traditional panel, you place a control (such as a button) in a specific location. You can freely move that control to any location within the panel using the mouse. In a flow panel, each control is placed in a specific location, regardless of where you place it with the mouse. The automatic location is controlled by the FlowStyle property. For example, using the default FlowStyle property of LeftRightTopBottom, the first control you add to the flow panel snaps to the top left corner. The second control that you add snaps next to the first control, and so on.

Related

Eclipse-like layout on Vaadin

I'm looking to design a page with separate sections on Vaadin7.
Earlier, i was convinced that each section as a Panel would do-- see Vaadin menu design - which component at the top? on this.
Now, i need each of these sections be minimized/maximized at user's will. So - when the user clicks the minimize icon, that section will disappear (be minimized) until its maximize button is pressed.
The overall design will be much like Eclipse.
What's the best design for this?
I can use Vaadin Window-s for minimize/maximize. but then, how do i manage their automatic placement in the overall layout?
I want the sections be resizable with respect to one another, just as in Panel-s.
If i use HorizantalSplitPanel/VerticalSplitPanel-s, how do i manage minimize/maximize?
One thing i can think of is:
split the overall layout into Panel-s so that each section will be a
Panel,
put a Window in each panel,
when minimize is pressed, make
that window-minimize event also trigger to minimize the Panel that
Window is in shrink as well.
How to achieve this? Is there a better way?
TIA.

TSpTbxDock vs TSpTbxMultiDock

In my current project I have a TTbxDock with toolbars and panels (TTbxDockablePanel). After moving TBX-->SpTBX I can't place a panel (TSpTbxDockablePanel) on a TSpTbxDock. I see a runtime error message saying I can't place a panel on a dock.
Do I need to use a TSpTbxMultiDock instead for panels? Why?
Can I place toolbars on a TSpTbxMultiDock instead of a TSpTBXDock?
i.e. why such code is written:
procedure TSpTBXCustomMultiDock.ValidateInsert(AComponent: TComponent);
begin
inherited;
if not (AComponent is TSpTBXCustomDockablePanel) then
raise EInvalidOperation.CreateFmt('Cannot insert %s into MultiDock', [AComponent.ClassName]);
end;
procedure TSpTBXCustomDockablePanel.ValidateContainer(AComponent: TComponent);
begin
inherited;
if (AComponent is TTBDock) and not (AComponent is TSpTBXCustomMultiDock) then
raise EInvalidOperation.CreateFmt('Cannot insert %s into %s. Place it on a MultiDock instead', [Self.ClassName, AComponent.ClassName]);
end;
Why can't I just use one type of dock?
"I need to use TSpTbxMultiDock instead for panels? Why?"
The short answer is, because the TSpTBXMultiDock component is written to handle and dock only TSpTBXDockablePanels.
The long answer is that the TSpTBXMultiDock component is written to handle and dock only TSpTBXDockablePanels because the docking code relies on specific methods / properties in the controls it is docking. See TSpTBXCustomDockablePanel.SetParent, which looks after this: it accesses the properties and methods TSpTBXCustomMultiDock.
UpdateDockablePanelsDockPos
ClientAreaWidth
ClientAreaHeight
In other words, the docking and layout code needs to know certain information and ask the docked controls to do certain things. The easiest way to do this is to constrain the type of the controls it can dock to descendants of a specific class which declares and implements the required interface (using interface loosely, there is no (class) interface declared, it's just an informal set of methods / properties.)
From scanning the code quickly, I think only the first of these, UpdateDockablePanelsDockPos, is really essential. I may have missed something. But this method gets a list of all docked panels and updates each one's DockPos, which is the one-dimensional position of the dock in the panel. That is, for a horizontal panel it is the left / horizontal start, and for a vertical panel it is the top / vertical start. It also updates the total so the panel knows how big is has to be, or can be if it chooses.
I'm not quite sure I've answered your question. I feel I've given a correct answer but not a helpful answer (the above are technical reasons, not conceptual reasons), which requires insight into what you're doing. My guess is you are asking this because you're struggling with a TBX -> SpTBX migration and you want docking areas which can handle both toolbars and dockable panels. Luckily, both these lead to your second question...
"Can I place toolbars on a TSpTbxMultiDock?"
I'm stepping into remembered territory here and I can't guarantee that this part of the answer is correct, because I converted our app from TBX to SpTBX a long, long time ago.
First of all, no, you cannot place a TSpTBXToolbar on a dockable panel. Nor can you dock a TSpTBXDockablePanel on a TSpTBXDock. A toolbar can dock into a dock, and a dockable panel can dock into a multidock.
The reason for this is guesswork, but I would guess it's because of the different behaviour of the two docked controls and their purpose.
Toolbars:
Are meant to contain SpTBX items (buttons, dropdown, etc, not any normal VCL controls, buttons, etc) and can dynamically change from being toolbars to menus etc, eg as the form is resized and the toolbar shrinks.
Stack: as well as having any amount of space between them (unlike docked panels, which always sit adjacent to each other) toolbars can have space between them; they sit whereever the user put them. They also often sit in several rows, and the one dock expands and contains several rows.
Are designed to shrink: a form can get too small or a user can drag a toolbar over another, and a toolbar will show only the items it can, and either hide the others or show a chevron (a tiny >> button to open a menu containing the other items.)
Dockable panels:
Are meant to hold complex controls, including standard VCL controls - they're meant as a toolbar and docking system-compatible way to build complex docked forms. (We design ours as frames and client-align a frame to the dockable panel.)
Rarely are on several rows. This is up to you as the UI implementer, but many panels docked next to each other could rapidly fill up form space. We keep LimitToOneRow turned on for dockable panels, so they they can only fill up one dimension (ie, on the left and right side of the form, dockable panels can only sit above and below each other, not to the left and right of each other.)
Resizing behaviour is different: they are always adjacent with no gap in between (toolbars can be positioned anywhere) and usually stretch one or more of the panel to fill the whole size of the dock (toolbars don't, unless MenuBar is set (see below)) and in fact toolbars are designed to shrink, not grow, and show a chevron and a drop-down menu. Panels don't have that behaviour.
After that diversion, back to your question. When we were converting our app I remember wondering what would happen if a user wanted to create a layout such that there were toolbars, then a dockable panel, and then toolbars again, in order to get as close as possible to mixing the two types. Doing so would require a dock, then a multidock, and then a dock again, but in practice this actually leads to users being able to create quite complex and confusing layouts. One problem with dockable UIs is that many users find them confusing (honestly, you'd be surprised), and can accidentally move or dock items where they don't want them. (It's good practice to contrain the menu bar to the top, for example - don't allow that one to be moved. It's just confusing.)
Our current design is that each side of our main window has two docks: one normal dock and then next to that that (closer to the center of the form) a multidock. The resulting behaviour is that all toolbars, such as menus and normal toolbars, can only dock next to the edge of the window. Dockable panels are then closer to the inside of the form than that. Keeping pairs of docks and multidocks next to each other on each form edge lets both types of controls be docked at any side of the form, but constrains toolbars to always be on the very edge of the form. This makes UI sense, actually: dockable panels tend to contain more complex things inside them than a toolbar, and it follows that they are more central controls. Of course, if one particular edge of the form has only a dockable panel on it, not a dockable toolbar, then the toolbar dock is invisible (width or height of 0) and so the panel sits next to the edge of the window too.
Written as text, it's hard to explain the previous two paragraphs (how this scheme works, and why this is okay / good.) You need to try it in practice and have a sense for user's behaviour. But basically, I recommend having both types of docks on your form, and keep the TSpTBXMultiDocks aligned 'inside' or 'inner to' the normal toolbar docks. Hopefully this image helps.
Other
You might be interested in the following properties:
DockableTo (toolbar and dockable panel): constrains which sides (top, bottom, left, right) a toolbar or panel can dock. Use this to make some controls only able to be docked in certain specific docks - a main menu to only dock on the top, for example.
DefaultDock (again, both): when users double-click to dock, rather than drag-docking, where does it go?
CurrentDock: I think you've already found this, but set it in the designer to move a toolbar or panel between docks, and read at runtime (or, I suppose, set at runtime if you programatically want to move things to specific docks.)
DockMode: controls whether a toolbar or panel can float (be undocked and be its own window), or not, including whether it can change docks. An example of using this is the main-menu constraint again.
FixedDockSize (panels only): stops the panel being resizable, either by the user or by the docking code referred to above.
MenuBar (toolbars only): gives the toolbar slightly different behaviour, I think that it always takes up the full width of the dock and doesn't allow other toolbars to be docked next to it. It may also change how items render slightly.

Forcing Vaadin Component to occupy it's space in invisible state

I have a Label and a Progess Indicator in my Vaadin indicator. It is dynamically made visible in the UI. There is a Tree below this Progress Indicator.
When the program dynamically sets the visibility of the Progress Indicator to true, the tree shifts down and the UI shakes due to the shifting.
Is there any way to make a Vaadin component occupy it's space, even if it is invisible and hence, when made visible it must not try to borrow space from other UI components?
What I am looking for is a feature similar to setRendered(true) in flex and actionscript programming.
Thanks for your help.
Finally I got an answer to my question. I just replaced the invisible components with a dummy visible label with no text.
And used it alternatively to switch between visible and invisible.
I asked the question in the Vaadin forum, and here's the response I got, from Kim Leppanen:
With Vaadin 7, if you set a component's visibility to false, then the component's information is not sent to the browser at all - it is just as if the component wouldn't exist in the layout at all.
I can quickly come up with two solutions. If you know the size of the component whose visibility you want to toggle, then you can use placeholder components - such as a Label. Put a label with the correct size in the place where you want the component. When you want to set a component as visible, then replace the label with the actual component.
The second option is to use css. Apply the css attribute "visibility: hidden" for the component you want to hide. Note that the component is not "truly" hidden. Let's say that it is a button. A user could still inspect the DOM tree and see the button in the code, change the visibility of the component on the client side (eg using developer tools or firebug) and then see and use the button as if it would be visible in the layout.
I am putting it here because people might add some more useful answers there. For a detailed explanation please see this.
Use this following example to the component you want set invisible but keeping its occupied space:
Image home = new Image();
home.setSource(HOME);
home.addStyleName("visibility: hidden");
OR
home.addStyleName("visibility: collapse");

Icon in header row for column setup

I would like to add an icon to the header of my data grid as it is done in Thunderbird.
There is an icon that is above the vertical scrollbar, no matter the position of the horizontal scrollbar. This icon allows the setup of the columns.
In Delphi there a lot of different grid components, that allow customizations and adding icons to there cells / header cells. But I could not find any component that has an area above the vertical scrollbar that is fixed, which when clicked allows some action. I could even use the VirtualTreeView component to emulate the grid, if it turns out to be easier to customize that component.
I am looking for some guidance on what need to be done to get that functionality.
Thanks,
Thomas
VirtualTreeView in Listbox mode would be nice, because of it's speed, great documentation and ease use in MVC-like patterns. Delphi tempts to store data in the visual components themselves, which letter causes troubles. While VTW allwos the same, it also allows to acutally separate data from GUI, and i like it.
But i am surprised by your claim "which when clicked allows some action.".
Even most basic components allow it:
http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Grids.TCustomGrid.OnFixedCellClick
So could you make more detaiils, why you cannot use standard components ? with screenshot and editors, how u want it rendered, where you want to click and what kind of action should happen ?

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