Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - CheatsOutlookBar.jpg

Introduction

I believe there's an old saying, "Clothes maketh the man", and this definitely applies to software too. No matter how clever and hard-working your applications are, if they're not wearing the latest well-designed clothes, people will think they are poorly written and out of date.

While working on a new project recently, I did a quick search on the web to see what the competition was up to. One of the features they had, which I didn't, was an Outlook-style menu bar, commonly referred to simply as an Outlook Bar. Deciding I wanted one too, I thought about writing my own. Searching the Code Project, I found two useful articles, Marc Clifton's An Outlook Bar Implementation, and Outlook XP bar by ACorbs. Both these articles were interesting and thought-provoking, and I downloaded each of them for a bit of a poke around, as one does!

I then found myself in a bit of a dilemma. I didn't want to simply copy someone else's code, but at the same time, I didn't want to spend hours and hours reinventing the wheel. I remembered a comment made by my manager at a previous job, many years ago. "What are we trying to do here? Achieve technical excellence, or get a job done?" Realising that Marc and ACorbs had done the former, I decided to go for the latter approach, and started doing some lateral thinking.

The eureka moment!

I proceeded to rummage around in the standard Windows Forms toolbox, and began to idly play around with splitters and panels. While doing this, I came across the Dock property, which specifies the justification for a control. In other words, does it attach itself to the top, bottom, left or right of its container, or does it fill all the available space. An idea occurred to me, so I added a Panel to my form, and added three Buttons to the panel. I set the Dock property of each button to Top, and "Lo!", I had created an Outlook Bar!

Well, almost...

Obviously, all I'd really done was stack three buttons one on top of the other, but they did resize themselves with the panel, which was decent of them. At this point, I realised I was going to have to write some code.

Some code

The only code I really needed is in the Click event handler for the buttons. They all share a common handler, as shown here:

        void ButtonClick(object sender, System.EventArgs e)
        {
            // Get the clicked button...

            Button clickedButton = (Button)sender;
            // ... and it's tabindex

            int clickedButtonTabIndex = clickedButton.TabIndex;

            // Send each button to top or bottom as appropriate

            foreach (Control ctl in panel1.Controls)
            {
                if (ctl is Button)
                {
                    Button btn = (Button)ctl;
                    if (btn.TabIndex > clickedButtonTabIndex)
                    {
                        if (btn.Dock != DockStyle.Bottom)
                        {
                            btn.Dock = DockStyle.Bottom;
                            // This is vital to preserve the correct order

                            btn.BringToFront();
                        }
                    }
                    else
                    {
                        if (btn.Dock != DockStyle.Top)
                        {
                            btn.Dock = DockStyle.Top;
                            // This is vital to preserve the correct order

                            btn.BringToFront();
                        }
                    }
                }
            }

            // Determine which button was clicked.

            switch (clickedButton.Text)
            {
                case "Cars":
                    CreateCarList();
                    break;

                case "Outlook Shortcuts":
                    CreateOutlookList();
                    break;

                case "Zip Files":
                    CreateZipList();
                    break;

                case "Miscellaneous":
                    CreateMiscList();
                    break;
            }

                        // Without this, the buttons will hide the items.

            listView1.BringToFront();
        }

Basically, when any of the buttons on the panel is clicked, we save the clicked button and its TabIndex, then go through all the controls on the panel in turn. If it's a button, we check the TabIndex against that of the clicked button. If the TabIndex is greater, then the current button must be below the clicked one, so we send it to the bottom by setting its Dock to DockStyle.Bottom. If the TabIndex is lower, then the button goes to the top.

At this stage, I should point out that to make this work, it's vital that the TabIndices of the buttons are set at design time to the correct order. In the supplied example, the "Cars", "Miscellaneous", "Zip Files" and "Outlook Shortcuts" buttons have their TabIndex set to 1, 2, 3 and 4 respectively.

Back to the main plot. Having set the Dock properties to send the buttons up and down, I noticed that all was not well. If I had buttons 1, 2, 3 and 4 at the top, and I clicked button 1, then buttons 2 to 4 went to the bottom, but they ended up in reverse order, like this:

All is not well...

In an attempt to correct this, I tried a few things, such as walking the panel's controls in reverse order, and altering the TabIndex values "on the fly", but without success. I decided to research the Dock property on MSDN, which told me about the z-order, and explained that it could be controlled by the SendToBack and BringToFront methods of a control. It's explained here and here, much more clearly that I can do in a few words. Suffice to say, by calling the SendToBack method on each button after I docked it, preserved the order of the buttons.

Finally...

The last piece of the jigsaw was another standard Windows Control, the ListView. I added this to the container panel, left its View property as the default LargeIcon, and set its Dock property to Fill, which means it will fill all the remaining space left by the buttons. I added some ImageLists, threw together some code to populate the ListView depending on which button was clicked, and I had a nice looking, functional Outlook bar, created from standard controls and a few lines of code. I then added a second ListView to provide some basic functionality to demonstrate that the menu bar worked.

Hopefully, this article will be of interest, and possibly show how sometimes it's useful to look at a problem from a few different angles before getting your code editor out!

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Questionauto hide
hesam1
4:10 18 May '09  
first of all thank you to share your experience.
then i would like to know how this menu bar can be auto hide with same buttons with VS 2008.(three buttons: close;autohide;window possition)
AnswerIts a great work
algates0027
21:36 31 Jan '09  
This is very fantastic work done..It has a very valuable point of explanation of Z order.. bring to front() fuction explanation
GeneralExcellent work
algates0027
21:34 31 Jan '09  
This is very fantastic work done..
Generalvb net?
jmarie
10:39 9 Mar '07  
Do you have code for this in vb? It is just what I need and looks really great, but I really need it in vb.

Thanks!
GeneralRe: vb net?
loboeire
12:08 30 Apr '07  
Private Sub ButtonClick(ByVal sender As Object, ByVal e As System.EventArgs)
' Get the clicked button...
Dim clickedButton As Button = CType(sender, Button)
' ... and it's tabindex
Dim clickedButtonTabIndex As Integer = clickedButton.TabIndex


' Send each button to top or bottom as appropriate
For Each ctl As Control In Me.Panel1.Controls
If TypeOf ctl Is Button Then
Dim btn As Button = CType(ctl, Button)
btn.BackColor = Color.FromName("GradientInactiveCaption")
If btn.TabIndex > clickedButtonTabIndex Then
If btn.Dock <> DockStyle.Bottom Then
btn.Dock = DockStyle.Bottom
' This is vital to preserve the correct order
btn.BringToFront()
End If
Else
If btn.Dock <> DockStyle.Top Then
btn.Dock = DockStyle.Top
' This is vital to preserve the correct order
btn.BringToFront()
End If
End If
End If
Next ctl
clickedButton.BackColor = Color.Gold


' Without this, the buttons will hide the items.
ListBox1.BringToFront()
End Sub
GeneralExcellent work!
sekasi
12:27 11 Sep '06  
Thanks for your time , this article is all i needed to get rolling.Cool
GeneralA problem
zohre_j
19:27 18 Jul '06  
First i want to thank you for your great work, and second i want to mention a problem i have when testing it in a code of my own :
in the code I have 2 panels tyhe first one which contains the outlook bar is on the above and the second one is under the first one.
when i click a button the other buttons go to the end of the page regardless of the panel size, and end up behind the second panel, why is that?
GeneralExcellent
Ami Drutman
4:31 20 Jun '06  
Thank u very much! I was looking for a "component" like this. Thank u again.

Ami Drutman.
Argentina.
GeneralSupport VB.NET??
nickong
20:51 13 Jun '05  
good job. support VB.NET ?


let's talk about code ^6^
GeneralProblem.
eirikhm
12:59 14 Mar '05  
I'm getting a InvalidOperationException thrown at me when I hit the switch statement. Been looking around trying to find a solution for it, but no luck so far. Can't really understand why this happens, since the control collection isn't really modified?

Part of the JIT message:

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.FastArrayListEnumerator.MoveNext()
at MeshMan.frmMain.ButtonClick(Object sender, EventArgs e)


EDIT:
Using Visual C# 2005 Express Edition
Eirik Hoem
GeneralRe: Problem.
eirikhm
13:10 14 Mar '05  
problem solved. seems like using foreach on collections and then enumerating is a bad thing. i tried using a for-loop and it worked.

for (int i=0;i{
Control ctl = pnlHotkeys.Controls[i];
// ..
// ..

Eirik Hoem
GeneralRe: Problem.
Gary Perkin
22:20 14 Mar '05  
Hi Eirik,

Thanks for your message. In fact, I found this problem myself after I posted the article. It's odd, because it doesn't always throw the exception. It might be to do with different versions of the .NET Framework - I use different versions at home and at work. When I get a moment, I'll update the code.

Regards,
Gary.
GeneralRe: Problem.
eirikhm
0:51 15 Mar '05  
No problem. Kudos for a simple yet elegant design.

Eirik
GeneralProblem with the samples
benjymous
4:56 10 Mar '05  
Just a small quibble to what seems to be an otherwise great article:

I tried to run the demo application, but just get a "System.Reflection.TargetInvocationException" exception

and the Source package just contains a single .cs file, without any project settings, so I can't instantly compile it myself without having to set up a project for it, which is a shame

[edit] I created a solution, but it still fails to run due to missing resources [/edit]

Otherwise good work, though Smile

--
Help me! I'm turning into a grapefruit!
Phoenix Paint - back from DPaint's ashes!

GeneralRe: Problem with the samples
nikster
14:10 11 Mar '05  
I had this problem too, it's the same reason the standalone exe didn't work.
Comment out the three "this.*.ImageStream =" lines and it will work, albeit without icons.

Nice article, very well done. 5* Big Grin
GeneralRe: Problem with the samples
nikster
14:17 11 Mar '05  
Damn modify doesn't work.

I'm guessing you need Outlook installed to get the icons as well, obviously, which I don't have Wink
GeneralThat's great,You are the man
xiayan
19:55 9 Mar '05  
thanks a lot , I have look for this for a long timeSmile :->
GeneralNice!
Adam Goossens
0:10 6 Mar '05  
Ahh, the simple solutions are always the most gratifying Big Grin

Fantastic! 5/5.

Office Automation - isn't.
GeneralZ-Order
Mark Treadwell
16:50 5 Mar '05  
You can control the z-order without relying on MoveToFront and MoveToBack. If you go to the buttons' container, you will find a Controls property. It's SetChildIndex method can then independently set the z-order of its contained controls.

MSDN describes it here[^].

Mark Treadwell
Special Enhancements

GeneralDoh Moment
Gary Thom
9:04 5 Mar '05  
Nice, it's a good example to all of us to step back and K.I.S.S... OMG

Gary
Rich Cook: "Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning."
GeneralWonderfull !
Wouter van Vugt
6:27 3 Mar '05  
Great work!

Wouter van Vugt

Trainer / Consultant - Info Support
http://www.infosupport.com

GeneralTechnical excellence vs. getting the job done
Marc Clifton
4:19 3 Mar '05  
Sometimes (more often than not, I tend to think), one can achieve both.

What a great approach!

Marc

MyXaml Advanced Unit Testing YAPO

GeneralRe: Technical excellence vs. getting the job done
WillemM
0:52 7 Mar '05  
I agree, this is an excellent implementation of a simple outlookbar. And with that I mean simple in the way of the technical approach. Looks great!

WM.
What about weapons of mass-construction?
GeneralEloquent Solution
Michael Potter
4:16 3 Mar '05  
Thanks - sometimes the easiest solutions are the most elusive.
GeneralRe: Eloquent Solution
austinf
7:07 3 Mar '05  
Yes very nice


Last Updated 3 Mar 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010