Tabbed Interface (MTI) For Your MDI Delphi Applications
Use Tabs to Navigate Through MDI Child Forms in your MDI Delphi Application
By Zarko Gajic , About.com Guide
See More About:
MTI For Your Delphi MDI
In an MDI application , more than one document or child window can be opened within a single parent window.
MDI, as a preferred user interface for applications that allow multiple documents to be contained within a single window, is being abandoned by Microsoft during the last few years. Microsoft started to promote a different user interface for such applications - TDI: Tabbed User Interface.
MDI application acts as a mini-desktop where windows (mdi children) can be minimized, maximized, arranged.
TDI application, similar to MDI, displays child windows under a single parent window. Unlike the floating child windows of MDI, in TDI each child is displayed in its own tab.
Browsers like IE8, Chrome or Firefox use tabbed user interface.
I have MDI, how do I convert it to TDI?
If you are like me, you have many MDI applications that you have developed through time. Breaking the entire logic of your application just to have a different user interface is not something you would be happy to do.
How about "simulating" a TDI for your MDI?
In most MDI application that I am developing users tend to have their active MDI child windows maximized. Most of the time, when a user needs another MDI child - it will get displayed maximized also - thus "hiding" the previously used child window/document.
In most cases, the Window menu of an MDI application also includes a list of the MDI child windows. This Window menu can be used to easily switch between MDI child windows / documents.
Here's an idea:
Let's place a TTabSet aligned Top inside the main MDI form: TMainForm.
When a new MDI child is created a new tab will be added to the Tab Set.
When a MDI child is destroyed, its associated tab on the TabSet will be removed.
When an MDI child window is activated, its tab will appear selected.
When a tab is (manually) selected - the "associated" MDI child will get displayed.
If the idea would work - we have a Tabbed User Interface on top of our MDI :)
Follow the steps to have the above working for your MDI application:
1. Drop a TTabSet control onto your MDI parent form. Set the name to "mdiChildrenTabs". Set the Align property to alTop. You can also set the Style property to tsModernTabs.
2. Have two public procedures MDIChildCreated and MDIChildDestroyed that will get called whenever an MDI child form is created or destroyed:
//add a tab for an MDI child procedure TMainForm.MDIChildCreated( const childHandle : THandle); begin ~TObject(childHandle));~:= -1 + mdiChildrenTabs.Tabs.Count; end ; //remove a tab for this MDI child procedure TMainForm.MDIChildDestroyed(const childHandle : THandle); var ~Integer; begin ~:= mdiChildrenTabs.Tabs.IndexOfObject(TObject(childHandle));~ end ;
3. Have a base form that all your MDI children forms inherit from. Let's call it "TBaseChildForm".
4. Implement the OnCreate event and OnDestroy as follows:
//notify main form about a new MDI child form procedure TBaseChildForm.FormCreate(Sender: TObject); begin ~ end ; //notify main form that a MDI child is destroyed procedure TBaseChildForm.FormDestroy(Sender: TObject); begin ~ end ;
5. Next, we want to ensure that when a tab is clicked on the TabSet, its "associated" MDI child form is activated. Handle the OnChange event of the TabSet:
//handles mdiChildrenTabs OnChange procedure TMainForm.mdiChildrenTabsChange(Sender: TObject; NewTab: Integer; var AllowChange: Boolean); var ~Integer;~Integer; begin ~:= Integer(mdiChildrenTabs.Tabs.Objects[NewTab]);~~ if mdiChildrenTabs.Tag = -1 then Exit;~~ for k := 0 to MDIChildCount - 1 do ~~ begin ~~~~ if MDIChildren[k].Handle = cHandle then ~~~~ begin ~~~~~~~~~~~~~~ end ;~~ end ; end ;
6. One more step and we are done. The above code ensures that our MDI child windows are activated when their tab is selected. We also need to ensure that a tab is selected when an MDI child window is activated by some other means: for example by using the Window menu, or from code.
The Implementing OnActivate / OnDeactivate for MDI Child Forms gives us the idea. We need the OnActiveMDIChildChange event!
Here's the implementation of the WM_MDIACTIVATE message handler:
procedure TBaseChildForm.WMMDIACTIVATE( var msg: TWMMDIACTIVATE); var ~TWinControl;~Integer; begin ~:= FindControl(msg.ActiveWnd) ;~~ if Assigned(active) then ~~ begin ~~~:= MainForm.mdiChildrenTabs.Tabs.IndexOfObject(TObject(msg.ActiveWnd));~~~:= -1;~~~:= idx;~~~:= 0;~~ end ; end ;
That's it, all handled. I'm using the Tag property of the TabSet to ensure that the code in the OnChange event handler is not executed when a MDI child is activated "by hand". MDI child window handle is stored along with its Caption as an Object in the Tabs (TStrings) property of the tab set: Store a String (or an Object) Along with a String in TStrings .
Note: you could turn to owner drawing and have those tabs look nicer with images :)