|

Introduction
I have read a lot of articles here on the CodeProject site and have greatly appreciated the work and ideas that everyone here has shared, so I decided to share this control as payback. The MdiTabStrip was developed out of the need to have a tabbed interface for an MDI application I am working on. I really liked the look and functionality of the Internet Explorer 7 tab control, so this became the basis for my control. It is not an exact replica, but it has a lot of similar features, as you will see in the demo. The only things missing that I wish could be included are the buttons and drop-down buttons of the IE7 control. I have a working version of a control that inherits from the ToolStrip component, but because the ToolStripDesigner class is not public and thus cannot be inherited, I can't add the designer experience I want for the tabs. I may post it one day if someone requests it, but for now it is still a work in progress.
My testing environment is very narrow, so I will appreciate any feedback as to how the control performs. Also, if there are any comments, suggestions and even complaints that you want to share, by all means do so. If there is a better way of doing something, I am always interested in learning. I hope someone finds this control useful. If not in whole, then maybe some part of it.
Background
This control is based on a windowless concept in which the control and its constituent parts only use one window handle. The tabs for each MDI child window and the tabs for scrolling appear to be separate controls that act independently of each other. However, the painting, layout, mouse events and drag-drop events are all handled by the container control. It was after reading an article here on CodeProject by Alexey A. Popov: "Extending Windows Forms - lightweight views" and studying the ToolStrip control using Lutz Roeder's .NET Reflector that I began to understand the power and flexibility of a windowless control. When looking at the IE7 tab control, I realized that the best way to reproduce it would be to use this method.
Using the code
There are really only two things you need to do to be up and running with this control. Add it to your form and set the form's IsMdiContainer property to True. Since the MdiTabStrip hooks into the parent form's MdiChildActivated event, it will automatically add a tab to its collection when a new form is opened as an MDI child. If this event is raised and a tab already exists for the child form, then this tab is made active. Each tab ties itself to its related form and hooks to this form's FormClosed event. It is through this event that the tab is removed from the collection.
Points of interest
Besides duplicating -- although not exactly -- the visual look of the IE7 tab control, it also duplicates most of its functionality. You can set a maximum and minimum width for the tabs. These settings come into play when you resize the form. The tabs will scale their size according to the amount of space available. When their size is below the set minimum, they start to disappear. This is when the scroll tabs become visible.
You can rearrange the order of each tab by dragging and dropping them to a new location. The control draws indicators as to where the tab will be positioned if dropped at the current location of the mouse pointer. I also included a custom cursor for the drag operation.
With the drop-down menu tab, you can select which form to activate via a menu. The currently active form's menu item is checked and when the tab for the form is currently hidden -- because there is not enough room to display it -- the background of the menu item is a light gray.
I have added a lot of customizability to this control, although I am sure I left something out that someone will ask for. There are basically three "types" of tabs: Active, InActive, and MousedOver. For these tab types, you can set the tab background color, foreground color, border color and font. I had hoped to create a designer that was similar to the ToolStrip's designer where, as you clicked on each item of the ToolStrip, the Property Browser window would show that item's properties. After studying the designer with .NET Reflector, I found that the switching between components was done via a method that was not public. So, giving up on this, I created a designer that used a form with a PropertyGrid control and used my own logic to set the grid's SelectedObject property.
I commented the code as much as I could to try and explain what was being done and why. I recommend opening the included demo and trying out the control. Some things are better understood seeing them in action than reading about them.
History
- 5 Jan 2007 - Original version posted.
- 8 Jan 2007 - Updated demo download.
- 16 Apr 2007 - Version 1.5 - Fixes and enhancements.
- Enhancement - Added a drop shadow behind the close button glyph. This gives the glyph a little more dimension.
- Fix – Although not truly a bug, the resizing of the control did not behave correctly. The proposed width for the tabs was based on whole numbers. Let's say you had four tabs open and you were resizing the parent form smaller and smaller and you got to the point where the proposed width for each tab was smaller than the minimum width allowed. You would have to resize the window 4 pixels before the tab width would scale again. If you had ten tabs open, it would take 10 pixels before the tabs would resize. This update calculates a proposed width the same way as before but also takes into account any remainder left over from this calculation and evenly distributes it between the tabs.
- Changed the
Text property for the MdiTab class. It now shadows the Control.Text property and returns the text of the Form it is tied to. Setting the Text property does nothing.
- Added the following events:
CurrentMdiTabChanged
MdiTabAdded
MdiTabClicked
MdiTabIndexChanged
MdiTabRemoved
- Added two new classes to handle the event arguments for the above events:
MdiTabStripTabClickedEventArgs and MdiTabStripTabEventArgs.
- The
MdiTabStrip will now correctly draw the image set as the BackgroundImage.
- Due to so many requests, I decided to add in a fix for the display issues noted when opening the first form, when switching between tabs and the control box displaying when no
MenuStrip is on the parent form. Although this is not a problem with the MdiTabStrip control and these problems still occur even if this control is not on the form, I decided to incorporate the fix that was suggested by Patrick Sears. Thank you, Patrick. I added a new property called MdiWindowState that has two possible values: Normal and Maximized. When set to Normal, you control each form's appearance through its properties. When set to Maximized, the MdiTabStrip will change each form's properties concerning the form's border, control box and docking.
- 22 Apr 2007 - Fixes and Enhancements.
- Added support for
RightToLeft.
- Fixed bug where if the Form's
Text property was changed, the tab's text did not update correctly and the drop-down item's text did not update at all.
- 6 Jul 2007 - Version 1.5.5 Fixes and Enhancements.
- Added
NewMdiTab class
- Class exposes event named
OnMdiNewTabClicked. A developer can handle this event to open a new form of choice.
- Three new properties that are used for this class:
MdiNewTabImage, MdiNewTabVisible and MdiNewTabWidth. If no image is specified then a default one is used.
- Added tooltips for tabs.
- Two new properties for this feature:
ShowTabToolTip and NewTabToolTipText.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 123 (Total in Forum: 123) (Refresh) | FirstPrevNext |
|
 |
|
|
When I create a new tab using this (excellent) control, the new tab steals focus. Is there any way to prevent this? Right now I am doing this:
If Not focusedBrowser Is Nothing Then Dim previousTab As Form = focusedBrowser.Parent Me.SuspendLayout() FormX.Visible = True previousTab.Activate() Me.ResumeLayout() Else Me.SuspendLayout() FormX.Visible = True Me.ResumeLayout() End If
This works fine, except I get some stupid quick flicker. I know I shouldn't be switching back to the previous tab and I need to figure out a way to just not switch to the newest tab. Any thoughts? Is this something I need to tear apart the DLL for?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,
Couple of questions;
If i have three tabs running (using three forms) and the user presses a button on tab 2 (form2) can i disable the other tabs 1 and 3 ? not close them just mke it so that the user must press another button on form 2 to enable the other forms ?
Thanks,
ANdy
NOTE; Thansk for the great component
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
Hi crcrites,
First of all it is brilliant control and it works really well. thank you.
I am getting a run time error which i am not able to resolve please could you have a look for me.
See the end of this message for details on invoking just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text ************** System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index at System.Collections.CollectionBase.System.Collections.IList.get_Item(Int32 index) at TabWindowView.MdiTabCollection.get_Item(Int32 index) at TabWindowView.MdiTabStrip.OnPaint(PaintEventArgs e) at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs) at System.Windows.Forms.Control.WmPaint(Message& m) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ScrollableControl.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
many thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
First off, fantastic control! I would also like to know how this control is licensed? Also, has anyone used it enough to see the big red X that occurs sometimes. It seems to be an irrecoverable error, but I can't quite figure it out. Thanks, Adam
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
If I close a form using CTRL+F4, the related tab stays on without the close button or caption. Is there anything I am doing wrong. Can you suggest the best configuration for the child forms that would have minimum issues? e.g. Maximised/Normal, Sizable or None borders etc.
Great control over all.
Thanks, TM
modified on Wednesday, January 23, 2008 6:23:08 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
First and foremost i want to say thank you for such a great component, however i've had some problem while using it, when you put the tab somewhere else than on the form's top left the form isn't added in a tab anymore in fact it's showed on the form's top left so i wanted to know how i could add it to a tab even though it's not on th top left anymore.
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Is there anyway to have a close button on the right upper corner rather than on each tab?
Thanks in advance.
Jason Law
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hallo crcrites, thank you for your exciting article and your great MDITabStrip - Control. Your control is quite a good piece of work and I would appreciate to use it in at least one application I am writing. The pitty is, that you did not reffer to any copyright or license. The application I am working on may become a commercial one so I would like to know whether I may use the MDITabStrip and if I am allowed to do so, under which license? Greetings Freebreaker
PS: Sorry for writing a public message, but I could not find a way to send a private message nor figured out your email.
|
| Sign In·View Thread·PermaLink | 2.25/5 (3 votes) |
|
|
|
 |
|
|
Hi, first of all thanks for this great control!
When I try to create a new tab, by default, the tab is added at the end. Is it possible by coding to add it at a specific location ?
For example, if I have 2 tabs opened I would like to insert it in between.
Any suggestion?
Thanks,
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
First of all, awsome work, and im glad someone with the know how took the time to do this. I would do it if i could, but im not that smart yet. Anyway, I wanted to have a tool strip container so i can drag menus around the main form. Whenever i add one, and then re-parent the control, it lays on top of the mdi container portion. Is there a way to do this with an mdi container?
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
Thanks for a great control!
The only problem I am experiencing is that I need the child forms to remain maximised.
When changing between tabbed windows my forms are restoring themselves, is there anyway to force the windows to remain maximised?
Rob
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I seem to have found the answer.
I have set the MdiWindowState to Maximised and this looks like it has worked.
Rob
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Similarly i need to be able to switch this during run time, Im not sure what the code would be. i tried this:
MdiTabStrip1.MdiWindowState = MdiTabStrip.MdiChildWindowState.Normal
It compiles, but doesnt change it to normal
-- modified at 15:00 Friday 31st August, 2007
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Ok,
Here's what I got:
Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim frmb As New frmBrowser frmb.MdiParent = Me frmb.ceBrowser.LocationUrl = "www.google.com" frmb.Show() End Sub
frmBrowser is a Form and has a webbrowser control on it. From frmMain, how do I access the properties of that tabbed browser control?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
'Change testbox1 on the active tab to say hey Dim tmpForm As frmBrowser = DirectCast(Me.ActiveMdiChild, frmBrowser)
tmpForm.textbox1.text="hey"
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
' Close all child forms of the parent. For Each ChildForm As Form In Me.MdiChildren ChildForm.Close() Next
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
Hi, once again, this is great job.
What I'm missing is ability to use it like excel tabs on the bottom (dock bottom). This means each one tab should paint upside-down - it would "stick" then to the window properly.
A property to set "orientation" would be just perfect 
Could you do that, please?
/PL01
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
I have a persisting tab issue. If I have one tab open, and then click the close button, the form and the tabitem goes away. However, if I have multiple tabs open, then one or more tabs persist, while the form closes. The persisting tabs are unclickable, have no close button, but still have the hover animation enabled, and tabs added after closing are placed after the persisting tab(s). This may be a known issue, but I'd want to know if you or someone has a fix before I go and mod (possibly ruin) the source. Other than that, this code has been a dream on cloud nine. Love the properties, methods, and events. Couldn't be better. Except for the.... well... if you don't know what I mean, read this article again. Thanks.
"Life you may evade, but death you shall not" -T.S. Eliot
|
| Sign In·View Thread·PermaLink | 2.00/5 (2 votes) |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|