c++ builder MDI / SDI or other approach? - c++builder

i wanna make a windows database application with c++ builder. The idea is to have a static menu of 6 icons at the top (i need this to be constant in every screen) while the rest of the screen will host all user interactions and data regarding with the selected menu item. I have a litte experiece with SDI apps and as far as i know there is no way the whole application to be in a single screen / form. Should i build this like an MDI app or is there any other way to maintain a fixed icon based menu at top while the rest screen data to change for every different menu item? I dont want to be in a single window with no overlaping forms while user navigates through the application.

Although an MDI application is definitely possible, the interaction between the different forms sometimes is a little cumbersome. A tabbed page is easier to handle since everything then resides within the same TForm class.
If you want to change the appearance of the individual tabs you can overload the 'PageControlDrawTab' Just add an event handler, get a handle to the Canvas of the tab itself and you are free to draw is as you want. See the example below:
void __fastcall TMainForm::PageControlDrawTab(TCustomTabControl *Control,
int TabIndex, const TRect &Rect, bool Active)
{
/* OnDraw handler to change the appearance of the Tabs.
Change it to blue text on white background.
*/
String s;
TRect r;
TTabControl * tTab = (TTabControl *)Control; // Get a pointer to the tab itself
s = tTab->Tabs->Strings[TabIndex]; // Retrieve the text of this tab
Control->Canvas->Brush->Color = clWhite; // Use the Canvas to draw
Control->Canvas->Font->Color = clBlue; // .. whatever you like
Control->Canvas->FillRect(Rect);
Control->Canvas->TextRect(Rect,Rect.Left+4,Rect.Top+2,s);
}

You will probably have to do it in a MDI format. I do not know of any way to share the menu across forms. The other option you could use though is to use a page control and have all the other "forms" live in a tab so the menu is the same all the time. The menu items could respond differently if you would like them to when the user is on a different tab, or they could do the same thing no matter what tab you are on. Sorry this is the form of an answer, I don't have comment rights yet.

Related

Make popup window close when releasing mouse button on an item on the fly

When clicking on a button I open a popup menu, e.g. using popupMenu.popup().
To select an item I must first release the mouse button and then click on a menu item to execute it.
This is "normal", but what I want is the behavior that I won't have to do an extra click on the menu item. I would like to be able to click on the button, move over a menu item (still holding the button) and execute it immediately after releasing the mouse button.
This is similar to how cascaded sub menus work.
I tried the way using TrackPopupMenu to hook into the messages and execute the item when the button is released. This works, but...
When using images in the popup menu (either bitmap or imagelist items) together with TrackPopupMenu then the menu does not show any entry. Every entry is some pixels wide and empty. You can use them blindly, so they work somehow but the drawing is not done correctly.
I'm using Delphi XE2 Pro.
I also tried to find general articles to hook into menu messages but did not find anything that works without using TrackPopupMenu. Maybe there is a way to have TrackPopupMenu displaying menus with images?
Any help is much appreciated.
You are routing menu messages to the wrong window procedure, you are passing the handle of your form to TrackPopupMenu (you should post your code, then there would be no need to guess what you're doing wrong. And you'd probably receive a much quicker reply).
Forms have menu support for window menus. When a form window procedure receives a, say WM_DRAWITEM, it only draws the item if it belongs to the window menu. For popup menus, VCL uses a utility window which is accessible through the global PopupList. See below example.
var
Pt: TPoint;
begin
Pt := Button1.ClientToScreen(Point(0, Button1.Height));
TrackPopupMenu(PopupMenu1.Handle, TPM_LEFTBUTTON, Pt.X, Pt.Y, 0,
PopupList.Window, nil);

Advancing control focus in a VCL TFrame

I have a TFrame on which some TEdits are placed. These edits are
boxes for serial key input, as I'm trying to setup a user experience
where input focus jumps from one edit box to the next, when a certain amount of
characters been entered in each. That is, user do not need to press tab
or click on the next edit in order to advance.
I found an example in the C++ Builder HowTo book (great book) on how to
"simulate" enter press to behave like a tab press in edits and was
trying to employ the same technique. However, something in my app don't
work as in that example.
In the frames KeyPress event, I have the code
void __fastcall TAboutFrame::Edit1KeyPress(TObject *Sender,
System::WideChar &Key)
{
TEdit* theEdit = dynamic_cast<TEdit*>(Sender);
if(!theEdit)
{
return;
}
if(theEdit->Text.Length() >= 6)
{
//jump to next edit
Perform(WM_NEXTDLGCTL, 0, 0);
...
But the 'jump' to next control does not occur.
The main Form, the frames parent, do have key preview == true and I can
set a breakpoint to see that the Perform call is indeed executed.
The tab orders on the edits are 1,2,3,4,5.
I wonder if this has todo with TFrames messaging or?
If the controls you are using descend from TWinControl (Which they should if you are using stock VCL controls), You can also use TWinControl->SetFocus() to explicitly set the focus to the desired control.

How do I prevent menu and toolbar clicks stealing focus from a floating form?

I want to achieve the same effect as in Visual Studio 2010+, where if you float an editor on top of the main form, clicking on a main form menu or toolbar button doesn't cause the main form to steal focus; instead the floating window retains focus.
Without this it becomes impossible to invoke any main menu command that operates on a focused control when the focused control is in a floating window - because the control you wanted to operate on loses focus just as the command is invoked.
(Ironically, if you look closely at this image you'll see that the selection in the editor is muted, indicating that the editor control doesn't have focus. Visual Studio seems to be doing something slightly devious to achieve the illusion that the floating window is still focused.)
The solution is very easy if you are using TActionMainMenuBar and TActionToolBar.
First the menu bar: TActionMainMenuBar has a property AutoFocus, which is True by default. Set this to False and then clicking on the menu won't automatically give focus to the form containing the menu. Instead, the form that previously had focus will retain it.
Now the tool bar: TActionToolBar doesn't have an AutoFocus property. Instead you need to handle its OnMouseActivate event and return maNoActivate.
Note that returning maNoActivate from an OnMouseActivate handler doesn't work for all controls. Some controls such as TButton will give themselves the focus when clicked regardless of the return result from the OnMouseActivate handler. But in the case of TActionToolBar we do get the effect we are after.
It took me a while to find this though, and even searching Google for a page containing both 'AutoFocus' and 'OnMouseActivate' elicits no useful results (other than, presumably, this page once it gets indexed). So I hope this answer will help someone else.

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.

Delphi: suggests, ideas in a building of User Interface

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.

Resources