Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / C#
Article

Outlook XP Bar: An example implementation of VivantList

Rate me:
Please Sign up or sign in to vote.
3.80/5 (9 votes)
14 Nov 20047 min read 113.8K   837   50   16
This project is an example implementation of VivantList as an Outlook bar.

Sample Image - Outlook.png

Process

The first thing we need to do is identify the behavior of the Outlook XP tool bar. We start by a look over the whole control, then follow with a close look at the deepest controls, like the buttons, and work up to the Outlook bar itself. Because we already know how we are going to implement the main part of this (VivantList), the key is to work from the small end, but always have a general idea of what we're aiming for.

(Note: For this project, I am not going to program mouse dragging to rearrange the order of items in the bar.)

First, we notice there are two modes to this bar which closely resembles Explorer’s list view’s ‘Large Icons’, and ‘List’ modes. This should be easy enough to take care of later and it provides no problems on this level, so we continue looking. At a glance, it appears that buttons can be used with their FlatStyle = Flat. However, closer observation shows that the button lights up if the mouse is just over the text label. This will take some fancy footwork to use the Button control, because there are no public functions to pass mouse events to the System.Window.Forms.Button control. There are a number of things you can do here, but I like to keep things ‘.NET friendly’, so I’m going to find another solution.

RealOutlook.png

At this point, I decide to write my own button. Microsoft provided a friendly way to write your own button. There is a public class:

C#
System.Windows.Forms.ButtonBase

This class is useful for writing your own button, because it provides a lot of the default behavior. In addition, you can inherit from this class and still view the control in the form designer. I am sure you’re all familiar with what Visual Studio .NET 2003 does when you inherit from a System.Windows.Forms.Control like a Panel or TreeView. Just in case you’re not familiar with the problem, the form designer will not display the control for editing… and it causes some other strange problems… I will go into why this is so, some other time.

Despite all this, I found the ButtonBase class to be cumbersome. In about thirty minutes, I whipped up a control to serve my needs, which inherits from the UserControl class.

C#
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace DemoOutLookBar
{
    /// <summary>
    /// Summary description for CustomButton.
    /// </summary>
    public class CustomButton :System.Windows.Forms.UserControl
    // System.Windows.Forms.ButtonBase
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;
        private System.Drawing.Icon m_Icon;
        private bool m_MouseIsOver = false;
        private bool m_MouseIsDown = false;

        public CustomButton()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();
            // TODO: Add any initialization after the InitializeComponent call
            this.SetStyle(System.Windows.Forms.ControlStyles.DoubleBuffer, true);
            this.SetStyle(
              System.Windows.Forms.ControlStyles.SupportsTransparentBackColor, 
              true);
            this.SetStyle(
              System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);
            this.UpdateStyles();

        }

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Component Designer generated code
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            // 
            // CustomButton
            // 
            this.Name = "CustomButton";
            this.Size = new System.Drawing.Size(96, 96);

        }
        #endregion

        public void PerformOnMouseEnter(EventArgs e)
        {
            this.OnMouseEnter(e);
        }

        public void PerformOnMouseLeave(EventArgs e)
        {
            this.OnMouseLeave(e);
        }

        public void PerformOnMouseDown(MouseEventArgs e)
        {
            this.OnMouseDown(e);
        }

        public void PerformOnMouseUp(MouseEventArgs e)
        {
            this.OnMouseUp(e);
        }

        public void PerformOnLeave(EventArgs e)
        {
            this.OnLeave(e);
        }

        public void PerformClick(EventArgs e)
        {
            this.OnClick(e);
        }


        protected override void OnMouseEnter(EventArgs e)
        {
            m_MouseIsOver = true;
            base.OnMouseEnter (e);
            this.Refresh();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            m_MouseIsOver = false;
            base.OnMouseLeave (e);
            this.Refresh();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            m_MouseIsDown = true;
            base.OnMouseDown (e);
            this.Refresh();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            m_MouseIsDown = false;
            base.OnMouseUp (e);
            this.Refresh();
        }

        protected override void OnLeave(EventArgs e)
        {
            m_MouseIsDown = false;
            base.OnLeave (e);
            this.Refresh();
        }


        protected override void OnPaint(PaintEventArgs e)
        {
            if (m_Icon != null)//If we have an image draw it
            {
                e.Graphics.DrawIcon(m_Icon, new 
                   System.Drawing.Rectangle(2, 2, 
                   this.ClientRectangle.Width - 3, 
                   this.ClientRectangle.Height - 3));
            }
            
            if (m_MouseIsOver)
            {
                e.Graphics.DrawRectangle(new 
                  System.Drawing.Pen(Color.FromArgb(10, 36, 106)), 
                  0,0,this.ClientRectangle.Width - 1, 
                  this.ClientRectangle.Height - 1);
                e.Graphics.FillRectangle(new 
                  SolidBrush(Color.FromArgb(75, 10, 36, 140)), 
                  1,1,this.ClientRectangle.Width - 2, 
                  this.ClientRectangle.Height - 2);
                if(m_MouseIsDown)
                    e.Graphics.FillRectangle(new 
                      SolidBrush(Color.FromArgb(50, 10, 36,140)), 
                      1,1,this.ClientRectangle.Width - 2, 
                      this.ClientRectangle.Height - 2);
            }
            base.OnPaint (e);
        }

        public System.Drawing.Icon Icon
        {
            get 
            {
                return m_Icon;
            }
            set
            {
                m_Icon = value;
            }
        }

    }
}

As you can see, this control will function like the Outlook flat buttons, but also has public functions to artificially fire some mouse functions. Side note: some colors were measured and some were approximated by comparison. To use our custom button, we will simply capture the Click event just like a System.Windows.Forms.Button.

Now that we know we can replicate our basic behavior, let’s take a closer look at the Outlook bar. Measuring in Adobe Photoshop, I found that the large icons were 32 X 32 pixels with a 36 X 36 pixel box going around them. Upon measuring the small icons, we find a small discrepancy. The icons are 16 X 16 pixels, but the button itself is only 19 X 20 pixels! There is a line of pixels missing on the right side of the button. I am choosing to correct for this by making the small buttons 20 X 20 pixels. I do not know if this was a mistake on the part of Microsoft or not, but this will be our first deviation from the original goal in this project.

Now we are faced with a choice. We know that we need to display two types of rows in our list. We know there will not be two rows of separate types on the list at once (not that this would be a problem). Finally, we know that in its heart, it is a control designed to emulate the list view control. We must now decide if we are going to design two separate row controls that inherit from VivantRow, or create one control that inherits from VivantRow that changes its layout based on its mode. Well, because of the final reason above, I decided to create one row control with modes. This way, the list can easily have modes like a Microsoft list view control. This means, all layout code must be done by hand, so for my ease, I made two layout functions. I start my new row control and place a label and a custom button on it.

C#
private void LayoutList()
{
    this.SuspendLayout();
    this.customButton1.Anchor = ((System.Windows.Forms.AnchorStyles)
      (System.Windows.Forms.AnchorStyles.Top |
      System.Windows.Forms.AnchorStyles.Left));
    this.customButton1.Location = new System.Drawing.Point(5, 3);
    this.customButton1.Size = new System.Drawing.Size(20, 20);

    this.label1.Location = new System.Drawing.Point(25, 0);
    this.label1.Size = new
      System.Drawing.Size(this.ClientSize.Width - 25 , 28);
    this.label1.TextAlign =
      System.Drawing.ContentAlignment.MiddleLeft;

    this.Size = new
      System.Drawing.Size(this.Parent.ClientSize.Width, 28);
    this.RowHeight = 28;
    this.ResumeLayout(false);
}
C#
private void LayoutLargeIcon()
{
    this.SuspendLayout();
    this.customButton1.Anchor = ((System.Windows.Forms.AnchorStyles)
      (System.Windows.Forms.AnchorStyles.Top));
    this.customButton1.Location = new
      System.Drawing.Point((this.ClientSize.Width/2) - (18 + 1), 6);
    this.customButton1.Size = new System.Drawing.Size(36, 36);

    this.label1.Location = new System.Drawing.Point(0, 46);
    this.label1.Size = new System.Drawing.Size(this.ClientSize.Width , 16);
    this.label1.TextAlign = System.Drawing.ContentAlignment.TopCenter;

    this.Size = new System.Drawing.Size(this.Parent.ClientSize.Width, 66);
    this.RowHeight = 66;
    this.ResumeLayout(false);
}

Next, we add a mode property that calls a function to perform the layout if the mode changed.

C#
private void LayoutRows(ViewMode Vmode)
{
    switch (Vmode)
    {
        case (ViewMode.Large_Icons):
            LayoutLargeIcon();
            break;
        case(ViewMode.List):
            LayoutList();
            break;
    }
    this.Update();
}
C#
public ViewMode Mode
{
    get
    {
        return m_Mode;
    }
    set
    {
            m_Mode = value;
            LayoutRows(m_Mode);
    }
}

Now that the row control is done, we move on to the sliding panels or tabs. For this, I took VivantWrapper, trimmed it down to minimum, and then added a button along the top. I called this control OutLookWrapper. I also dropped a couple of buttons down that will take care of scrolling. We’ll get back to them later.

Outlookwrapper.png

After some thought, I decided to make the control that holds these OutLookWrappers a VivantList. What we’ll do is make each one a row, and size it down to the size of the button when someone clicks on the button on another Outlook wrapper. I had originally planed something like this, combined with some tab control code I wrote a while back. But, I think a VivantList will work better here. So, we make OutLookWrapper inherit from VivantRow, and add a wrapper for the whole bar control.

Sizingcontrols.png

This is where I had stopped before. I made some time to finish this up ahead of schedule. A lot of code related to scrolling made things complex. Now, given that there are still a few minor bugs, I am going to leave that up to you to look over if you’re interested. However, I may explain it once it works completely.

Structure

From here, we are going to look at the overall structure. Once this structure is assembled, the rest is just debugging and testing. That said, I am going to jump from structure to the final product.

ControlStructure.png

Now that we have a visual representation of our structure, let’s look at some of the classes:

  • OutLookWrapper
    • Mode

      This gets or sets the display mode of the icons in the tab. This property's type is [Enum]ViewMode.

    • Text

      This gets or sets the text that appears on the button at the top of the tab.

    • AddRow(OutLookRow Row)

      This method adds an OutLookRow to the OutLookWrapper.

  • OutLookRow
    • Icon

      This gets or sets the icon that is displayed in the OutLookRow.

    • Text

      This gets or sets the text that is displayed below the icon in the OutLookRow.

    • SetAction(new ButtonClicked(ClikedBlank));

      This method takes a delegate that is the type of ButtonClicked. ButtonClicked acts as a function pointer for a function that takes no arguments. This function is executed when the icon or text is clicked on. This is in place of raising an event when the icon is clicked on. This is done this way for no other reason than my personal preference; it can easily be changed to an event.

  • OutLookBar
    • AddRow(OutLookWrapper Row)

      This method adds an OutLookWrapper to the OutLookBar.

Final Product

So, now that we have it all together, let’s see how to use it.

  • Place an OutLookBar on a form with the Forms Designer.
  • Create a new OutLookWrapper:
    C#
    OutLookWrapper m_OLW = new OutLookWrapper();
  • Set the text of the OutLookWrapper:
    C#
    m_OLW.Text = "ListView";
  • Create OutLookRows to fill the OutLookWrapper:
    • Create a function that you wish to be executed when the icon is clicked on.
      C#
      private void ClikedBlank()
      {
          MessageBox.Show("You Clicked Blank!!!");
      }
    • Create your new OutLookRow.
      C#
      OutLookRow m_OLR = new OutLookRow();
    • Set the icon for your OutLookRow.
      C#
      m_OLR.Icon =  new Icon("..\\..\\Icon1.ico");
    • Set the text for your OutLookRow.
      C#
      m_OLR.Text = "Blank";
    • Pass in the Action function for your OutLookRow.
      C#
      m_OLR.SetAction( new ButtonClicked(ClikedBlank));
    • Add your OutLookRow to your OutLookWrapper.

      C#
      m_OLW.AddRow(m_OLR);
  • Set the mode of your OutLookWrapper:
    C#
    m_OLW.Mode = ViewMode.List;
  • Add your OutLookWrapper to your OutLookBar:
    C#
    this.outLookBar1.AddRow(m_OLW.Mode);

And that's it!

I recommend you embed your icons into your program, rather than reading them from file as I have done above. For more information on a simple way to do this, see: Understanding Embedded Resources in Visual Studio .NET by James T. Johnson.

To see more, I recommend you download the sample project.

Future Plans

  • Add mouse wheel support.
  • Debug scroll buttons.
  • Maybe, add mouse dragging.
  • Test using background images on OutLookWrapper.

History

  • 11/14/04 - Initial upload.
  • 11/18/04 - Completed article and uploaded working control.

Final Words

Please, feel free to post any feedback you might have. Even if I don’t directly respond to it, I will read it. The feedback I get will guide the development of this or any project I do. Thanks.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Particle in a box

Comments and Discussions

 
GeneralI can add only tree OutLookRow Pin
xiaotianfan27-Mar-06 21:54
xiaotianfan27-Mar-06 21:54 
QuestionOutlook 2003 Style Pin
chaf270114-Nov-05 10:36
chaf270114-Nov-05 10:36 
Questionhow to setaction in vb.net Pin
Member 212013024-Jul-05 19:07
Member 212013024-Jul-05 19:07 
GeneralBug Fixes Pin
ACorbs22-Nov-04 19:24
ACorbs22-Nov-04 19:24 
GeneralLooks good Pin
WillemM19-Nov-04 0:32
WillemM19-Nov-04 0:32 
GeneralRe: Looks good Pin
ACorbs19-Nov-04 11:21
ACorbs19-Nov-04 11:21 
GeneralMissing Sliding animation Pin
NormDroid14-Nov-04 21:13
professionalNormDroid14-Nov-04 21:13 
GeneralRe: Missing Sliding animation Pin
ACorbs17-Nov-04 20:28
ACorbs17-Nov-04 20:28 
GeneralRe: Missing Sliding animation Pin
NormDroid17-Nov-04 20:52
professionalNormDroid17-Nov-04 20:52 
GeneralRe: Missing Sliding animation Pin
ACorbs17-Nov-04 21:04
ACorbs17-Nov-04 21:04 
GeneralRe: Missing Sliding animation Pin
ACorbs17-Nov-04 21:08
ACorbs17-Nov-04 21:08 
GeneralRe: Missing Sliding animation Pin
ACorbs18-Nov-04 17:32
ACorbs18-Nov-04 17:32 
GeneralFeel free to post suggestions and fixes Pin
ACorbs14-Nov-04 17:32
ACorbs14-Nov-04 17:32 
GeneralArticle is done Pin
ACorbs17-Nov-04 20:30
ACorbs17-Nov-04 20:30 
GeneralRe: Article is done Pin
NormDroid17-Nov-04 20:59
professionalNormDroid17-Nov-04 20:59 
GeneralRe: Article is done Pin
ACorbs17-Nov-04 21:12
ACorbs17-Nov-04 21:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.