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.
Related
Assume the following situation:
FrmBase has a TPanel named PnlClient, align alClient
FrmDescendant inherits from FrmBase
In FrmDescendant I change a PnlClient property
In FrmDescendant I place another control (say another TPanel named PnlDescendant) on PnlClient and align it alClient.PnlDescendant now completely covers PnlClient
I place lots of other components on PnlDescendant, not necessarily aligned, so I'm afraid to change PnlDescendant (e.g. setting align to alNone and resizing) and lose positions
How can I execute 'revert to inherited' for PnlClient?
There's nothing in the main menu, or in the popup menu for the controls in the Structure View...
Currently using Delphi 10.4.1 Sydney
This is easy for a keyboard user like myself:
Click on PnlDescendant in the form editor to make it the focused and selected control.
Press Esc to select its parent, PnlClient.
Press the Menu key on your keyboard to display the context menu of PnlClient. If you have a cheap keyboard without a Menu key, press Shift+F10 instead.
Click on "Revert to Inherited". (Or, much faster: press the menu item's underlined character, probably I.)
(But notice that this will remove PnlDescendant completely. Probably you didn't want that, but that's how it works.)
When I add, for example, a TPanel to a form and align it using alTop and then add a TMainMenu to the same form, the main menu position overrides the panel position such that the Main Menu remains topmost on the form with the panel appearing below it.
Is it possible to override the TMainMenu position so I can add a component like a Panel above it and the Main Menu below it?
Alternatives like TActionManager and/or TToolBar are not suitable because of BiDi compatibility issues.
No, the main menu is a Windows component and its location is fixed. This isn't a Delphi limitation but a restriction on how menus work with windows in Windows.
From MSDN (emphasis mine) :
Only an overlapped or pop-up window can contain a menu bar; a child window cannot contain one. If the window has a title bar, the system positions the menu bar just below it. A menu bar is always visible.
If you want menu-like behaviour in a component which you can freely place anywhere on your form, you would need to either write one yourself or look for third-party alternatives.
I would like to dvelop an app with a GUI like CCleaner, where you have a left aligned tabs, (but not the typically wicvh contain only a word) with an image in a button.
I couldn't find the properties, probably because i'm working only with the installation of delphi XE2, an i have only the TTabControl on the win32 pallete.
i don't know how to create a form like this i call above. Can it be created with a ttabcontrol?
Could anyone help me? I want a form with a left tab with button in each option, that change the right side of the form...
On the Win32 palette there are TTabControl (as you know) and TPageControl which is similar to TTabControl but has separate pages (called TTabSheet) for each tab.
Both have a property TabPosition with 4 possible values: tpTop (default), tpBottom, tpLeft and tpRight. The text of the tabs are vertical for tab positions tpLeft and tpRight. Both tab controls also supports images in the tabs through the Images property which can take a TImageList as container for the images.
If you don't like the vertical text on the tabs, you can compose your form with separate buttons on the left and panels or frames as pages. As buttons you can use TBitBtn or TButton buttons placed directly on the form or on a panel if you like, or you can use a TButtonGroup All of these supports the TImageList mentioned above. For some you can have separate images for disabled, hovered (hot), selected and pressed states.
Read more about these controls in the help.
I am building an user interface. My program will consist of 4 main parts:
1) Top Menu - TMainMenu. A top of a window
2) Main Menu - TTreeView. A left of a window. Each item of TreeView=corresponded TabSheet of TPageCotrol.
3) Work space - TPageControl. No tabs. An left space.
Each TabSheet has it's own ToolBar and other controls. It will be 5 menus (5 items in TreeView) = 5 TabSheets -> 5 ToolBars and other controls on each TabSheet.
It almost exactly looks like here (it could be: TreeView as Main Menu; MainMenu as Top Menu; Work Area - ToolBar and other controls):
I would like to use an user interface like here:
Where the CoolBar are represented with the Top Menu and the ToolBar (it suits my purpose to use 5 ToolBars instead of 1 ToolBar on each TabSheet). But after a discussion here it seems impossible to copy this CoolBar with the MainMenu and the ToolBar.
My question: how to build an efficient user interface using as examples the UI of uTorrent and the UI of Windows Firewall?
Are there good alternatives, ideas of a building of the UI? How would you make your interface if you need those 4 parts as me?
Now I have something like this:
Thanks!
You'll have to have 5 panels in your client area.
As you select a node in the treeview on the left you will need to call
pnlInboundRules.BringToFront;
The difficult thing is then how to update the single toolbar with the items that should appear on it based on what "tab" you've selected.
You would have to delete the toolbar buttons, and add new buttons for that "page".
Ian suggest panels, I would go with PageControl and set it's tabs TabVisible to false at runtime(*). The reason is that it is much more easy to manage at design time. When user select an item from treeview, activate corresponding TabSheet...
*) I actually haven't done this with stock TPageControl so I don't rememeber would it work this way, ie would it activate TabSheet which have it's TabVisible set to false. It might be that you have to set TabHeight to 0 or use some other trick to hide tabs at runtime.
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.