Click here to Skip to main content
15,897,704 members
Articles / Desktop Programming / Windows Forms

Storm - the world's best IDE framework for .NET

Rate me:
Please Sign up or sign in to vote.
4.96/5 (82 votes)
4 Feb 2010LGPL311 min read 278.5K   6.5K   340  
Create fast, flexible, and extensible IDE applications easily with Storm - it takes nearly no code at all!
//
//    ___ _____ ___  ___ __  __ 
//   / __|_   _/ _ \| _ \  \/  |
//   \__ \ | || (_) |   / |\/| |
//   |___/ |_| \___/|_|_\_|  |_|
// 
//   Storm.TabControl.dll
//   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//     Storm.TabControl.dll was created under the LGPL license.
//     It was based on another .dll and evolved from that one.
//     The original .dll was created by Hadi Eskandari.
//
//   What is Storm?
//   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//     Storm is a set of dynamic link libraries used in Theodor
//     "Vestras" Storm Kristensen's application "Moonlite".
//     
//
//   Thanks:
//   ¯¯¯¯¯¯¯
//     - Hadi Eskandari for creating and publishing the library.
//
//
//   Copyright (c) Theodor "Vestras" Storm Kristensen 2009
//   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//

namespace Storm.TabControl
{
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Threading;
    using System.Windows.Forms;

    using Storm.TabControl.Base;
    using Storm.TabControl.Design;

    [Designer(typeof(TabStripDesigner))]
    [DefaultEvent("TabStripItemSelectionChanged")]
    [DefaultProperty("Items")]
    [ToolboxItem(true)]
    [ToolboxBitmap("TabStrip.bmp")]
    public class TabStrip : BaseStyledPanel, ISupportInitialize, IDisposable
    {
        #region Static Members
        internal static int PreferredWidth = 350;
        internal static int PreferredHeight = 200;
        #endregion

        #region Constants
        private const int DEF_HEADER_HEIGHT = 19;
        private const int DEF_GLYPH_WIDTH = 40;

        private int DEF_START_POS = 10;
        #endregion

        #region Members
        private TabStripMenuGlyph menuGlyph = null;
        private TabStripCloseButton closeButton = null;
        private TabStripItemCollection items;
        private TabStripItem selectedItem = null;

        private Rectangle stripButtonRect = Rectangle.Empty;
        private Point cursorLoc = new Point(0, 0);
        private ContextMenuStrip menu = null;
        private ToolStripDropDownMenu rightClickMenu = null;

        private StringFormat sf = null;
        private static Font defaultFont = new Font("Tahoma", 8.25f, FontStyle.Regular);

        private bool alwaysShowClose = true;
        private bool isIniting = false;
        private bool alwaysShowMenuGlyph = true;
        private bool menuOpen = false;

        #region Hover & Color Members
        private Color selectedColor1;
        private Color selectedColor2;
		private Color selectedForeColor;

        private Color normalColor1;
        private Color normalColor2;
		private Color normalForeColor;

        private Color selectedBorderColor;
        private Color normalBorderColor;

        private TabStripItem currentHotItem = null;
        private TabStripItem hoverItem = null;
        #endregion
        #endregion

        #region Events
        public event TabStripItemMouseEnterHandler TabStripItemMouseEnter;
        public event TabStripItemMouseLeaveHandler TabStripItemMouseLeave;

        public event TabStripItemClosingHandler TabStripItemClosing;
        public event TabStripItemChangedHandler TabStripItemSelectionChanged;
        public event SelectedItemChangedHandler SelectedItemChanged;

        public event HandledEventHandler MenuItemsLoading;
        public event EventHandler MenuItemsLoaded;
        public event EventHandler TabStripItemClosed;
        #endregion

		#region Properties
		[DefaultValue(null)]
		[RefreshProperties(RefreshProperties.All)]
		public TabStripItem SelectedItem
		{
			get { return selectedItem; }
			set
			{
				if (selectedItem == value)
					return;

				if (value == null && Items.Count > 0)
				{
					TabStripItem itm = Items[0];
					if (itm.Visible)
					{
						selectedItem = itm;
						selectedItem.Selected = true;
						selectedItem.Dock = DockStyle.Fill;
					}
				}
				else
					selectedItem = value;

				foreach (TabStripItem itm in Items)
				{
					if (itm == selectedItem)
					{
						SelectItem(itm);
						itm.Dock = DockStyle.Fill;
						itm.Show();
					}
					else
					{
						UnSelectItem(itm);
						itm.Hide();
					}
				}

				SelectItem(selectedItem);
				Invalidate();

				if (!selectedItem.IsDrawn)
				{
					Items.MoveTo(0, selectedItem);
					Invalidate();
				}

				OnTabStripItemChanged(
					new TabStripItemChangedEventArgs(selectedItem, TabStripItemChangeTypes.SelectionChanged));
			}
		}

		[DefaultValue(true)]
		public bool AlwaysShowMenuGlyph
		{
			get { return alwaysShowMenuGlyph; }
			set
			{
				if (alwaysShowMenuGlyph == value)
					return;

				alwaysShowMenuGlyph = value;
				Invalidate();
			}
		}

		[DefaultValue(true)]
		public bool AlwaysShowClose
		{
			get { return alwaysShowClose; }
			set
			{
				if (alwaysShowClose == value)
					return;

				alwaysShowClose = value;
				Invalidate();
			}
		}

		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public TabStripItemCollection Items
		{ get { return items; } }

		[DefaultValue(typeof(Size), "350,200")]
		public new Size Size
		{
			get { return base.Size; }
			set
			{
				if (base.Size == value)
					return;

				base.Size = value;
				UpdateLayout();
			}
		}

		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public new ControlCollection Controls
		{ get { return base.Controls; } }

		public ToolStripDropDownMenu RightClickMenu
		{
			get { return rightClickMenu; }
			set { rightClickMenu = value; }
		}

		public Color SelectedColorStart
		{
			get { return selectedColor1; }
			set
			{
				selectedColor1 = value;
				this.Invalidate();
			}
		}

		public Color SelectedColorEnd
		{
			get { return selectedColor2; }
			set
			{
				selectedColor2 = value;
				this.Invalidate();
			}
		}

		public Color SelectedBorderColor
		{
			get { return selectedBorderColor; }
			set
			{ 
				selectedBorderColor = value;
				this.Invalidate();
			}
		}

		public Color NormalColorStart
		{
			get { return normalColor1; }
			set
			{
				normalColor1 = value;
				this.Invalidate();
			}
		}

		public Color NormalColorEnd
		{
			get { return normalColor2; }
			set
			{
				normalColor2 = value;
				this.Invalidate();
			}
		}

		public Color NormalBorderColor
		{
			get { return normalBorderColor; }
			set
			{
				normalBorderColor = value;
				this.Invalidate();
			}
		}

		public Color NormalForeColor
		{
			get { return normalForeColor; }
			set
			{
				normalForeColor = value;
				this.Invalidate();
			}
		}

		public Color SelectedForeColor
		{
			get { return selectedForeColor; }
			set
			{
				selectedForeColor = value;
				this.Invalidate();
			}
		}
		#endregion

        #region Methods
        #region Public
        public HitTestResult HitTest(Point pt)
        {
            if (closeButton.Bounds.Contains(pt))
                return HitTestResult.CloseButton;

            if (menuGlyph.Bounds.Contains(pt))
                return HitTestResult.MenuGlyph;

            if (GetTabItemByPoint(pt) != null)
                return HitTestResult.TabItem;

            return HitTestResult.None;
        }

        public void AddTab(TabStripItem tabItem)
        {
            AddTab(tabItem, false);

            if (SelectedItemChanged != null)
                SelectedItemChanged(new TabStripItemMouseEventArgs(SelectedItem));

            // Update the currently shown controls.
            foreach (Control control in SelectedItem.Controls)
                control.Show();
        }

        public void AddTab(TabStripItem tabItem, bool autoSelect)
        {
            tabItem.Dock = DockStyle.Fill;
			tabItem.TabStripParent = this;
            Items.Add(tabItem);

            TabStripItemMouseEnter += new TabStripItemMouseEnterHandler(OnTabMouseEnter);
            TabStripItemMouseLeave += new TabStripItemMouseLeaveHandler(OnTabMouseLeave);

            if ((autoSelect && tabItem.Visible) || (tabItem.Visible && Items.DrawnCount < 1))
            {
                SelectedItem = tabItem;
                SelectItem(tabItem);

                if (SelectedItemChanged != null)
                    SelectedItemChanged(new TabStripItemMouseEventArgs(tabItem));

                // Update the currently shown controls.
                foreach (Control control in tabItem.Controls)
                    control.Show();
            }
        }

        public void RemoveTab(TabStripItem tabItem)
        {
            int tabIndex = Items.IndexOf(tabItem);

            TabStripItemMouseEnter -= new TabStripItemMouseEnterHandler(OnTabMouseEnter);
            TabStripItemMouseLeave -= new TabStripItemMouseLeaveHandler(OnTabMouseLeave);

            if (tabIndex >= 0)
            {
                UnSelectItem(tabItem);
                Items.Remove(tabItem);
            }

            if (Items.Count > 0)
            {
                if (RightToLeft == RightToLeft.No)
                {
                    if (Items[tabIndex - 1] != null)
                        SelectedItem = Items[tabIndex - 1];
                    else
                        SelectedItem = Items.FirstVisible;

                    if (SelectedItemChanged != null)
                        SelectedItemChanged(new TabStripItemMouseEventArgs(SelectedItem));

                    // Update the currently shown controls.
                    foreach (Control control in SelectedItem.Controls)
                        control.Show();
                }
                else
                {
                    if (Items[tabIndex + 1] != null)
                        SelectedItem = Items[tabIndex + 1];
                    else
                        SelectedItem = Items.LastVisible;

                    if (SelectedItemChanged != null)
                        SelectedItemChanged(new TabStripItemMouseEventArgs(SelectedItem));

                    // Update the currently shown controls.
                    foreach (Control control in SelectedItem.Controls)
                        control.Show();
                }
            }
        }

        public TabStripItem GetTabItemByPoint(Point pt)
        {
            TabStripItem item = null;
            bool found = false;

            for (int i = 0; i < Items.Count; i++)
            {
                TabStripItem current = Items[i];

                if (current.StripRect.Contains(pt) && current.Visible 
                    && current.IsDrawn)
                {
                    item = current;
                    found = true;
                }

                if (found)
                    break;
            }

            return item;
        }

        public virtual void ShowMenu()
        {
            if (menu.Visible == false && menu.Items.Count > 0)
            {
                if (RightToLeft == RightToLeft.No)
                    menu.Show(this, new Point(menuGlyph.Bounds.Left, 
                        menuGlyph.Bounds.Bottom));
                else
                    menu.Show(this, new Point(menuGlyph.Bounds.Right, 
                        menuGlyph.Bounds.Bottom));

                menuOpen = true;
            }
        }

		public void CheckHot()
		{
			foreach (TabStripItem item in Items)
			{
				if (item.IsHot == false &&
					currentHotItem == item)
				{
					currentHotItem = null;
					item.IsHot = false;
					if (TabStripItemMouseLeave != null)
					{
						TabStripItemMouseEventArgs args =
						new TabStripItemMouseEventArgs(item);
						TabStripItemMouseLeave(args);
					}
				}
				
				if (item.IsHot == true &&
					(item.StripRect.Contains(PointToClient(Cursor.Position)) == false ||
					 item.Bounds.Contains(PointToClient(Cursor.Position)) == true))
				{
					currentHotItem = null;
					item.IsHot = false;
					if (TabStripItemMouseLeave != null)
					{
						TabStripItemMouseEventArgs args =
						new TabStripItemMouseEventArgs(item);
						TabStripItemMouseLeave(args);
					}
				}
				if (item.StripRect.Contains(PointToClient(Cursor.Position)) == true)
				{
					currentHotItem = item;
					item.IsHot = true;
					if (TabStripItemMouseEnter != null)
					{
						TabStripItemMouseEventArgs args =
							new TabStripItemMouseEventArgs(item);
						TabStripItemMouseEnter(args);
					}
				}
			}
		}
        #endregion

        #region Internal
        internal void UnDrawAll()
        {
            for (int i = 0; i < Items.Count; i++)
            {
                Items[i].IsDrawn = false;
            }
        }

        internal void SelectItem(TabStripItem tabItem)
        {
            tabItem.Dock = DockStyle.Fill;
            tabItem.Visible = true;
            tabItem.Selected = true;
        }

        internal void UnSelectItem(TabStripItem tabItem)
        {
            tabItem.Selected = false;
        }
        #endregion

        #region Protected
        protected internal virtual void OnTabStripItemClosing(TabStripItemClosingEventArgs e)
        {
            if (TabStripItemClosing != null)
                TabStripItemClosing(e);
        }

        protected internal virtual void OnTabStripItemClosed(EventArgs e)
        {
            if (TabStripItemClosed != null)
                TabStripItemClosed(this, e);
        }

        protected virtual void OnMenuItemsLoading(HandledEventArgs e)
        {
            if (MenuItemsLoading != null)
                MenuItemsLoading(this, e);
        }

        protected virtual void OnMenuItemsLoaded(EventArgs e)
        {
            if (MenuItemsLoaded != null)
                MenuItemsLoaded(this, e);
        }

        protected virtual void OnTabStripItemChanged(TabStripItemChangedEventArgs e)
        {
            if (TabStripItemSelectionChanged != null)
                TabStripItemSelectionChanged(e);
        }

        protected virtual void OnMenuItemsLoad(EventArgs e)
        {
            menu.RightToLeft = RightToLeft;
            menu.Items.Clear();

            for (int i = 0; i < Items.Count; i++)
            {
                TabStripItem item = Items[i];
                if (!item.Visible)
                    continue;

                ToolStripMenuItem tItem = new ToolStripMenuItem(item.Title);
                tItem.Tag = item;
                tItem.Image = item.Image;
                menu.Items.Add(tItem);
            }

            OnMenuItemsLoaded(EventArgs.Empty);
        }
        #endregion

        #region Overrides
        protected override void OnRightToLeftChanged(EventArgs e)
        {
            base.OnRightToLeftChanged(e);
            UpdateLayout();
            Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            SetDefaultSelected();
            Rectangle borderRc = ClientRectangle;
            borderRc.Width--;
            borderRc.Height--;

            if (RightToLeft == RightToLeft.No)
                DEF_START_POS = 10;
            else
                DEF_START_POS = stripButtonRect.Right;

            e.Graphics.DrawRectangle(SystemPens.ControlDark, borderRc);
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            #region Draw Pages
            for (int i = 0; i < Items.Count; i++)
            {
                TabStripItem currentItem = Items[i];
                if (!currentItem.Visible && !DesignMode)
                    continue;

                OnCalcTabPage(e.Graphics, currentItem);
                currentItem.IsDrawn = false;

                if (!AllowDraw(currentItem))
                    continue;

                OnDrawTabPage(e.Graphics, currentItem);
            }
            #endregion

            #region Draw UnderPage Line
            if (RightToLeft == RightToLeft.No)
            {
                if (Items.DrawnCount == 0 || Items.VisibleCount == 0)
                {
                    e.Graphics.DrawLine(SystemPens.ControlDark, new Point(0, DEF_HEADER_HEIGHT),
                                        new Point(ClientRectangle.Width, DEF_HEADER_HEIGHT));
                }
                else if (SelectedItem != null && SelectedItem.IsDrawn)
                {
                    Point end = new Point((int)SelectedItem.StripRect.Left - 9, DEF_HEADER_HEIGHT);
                    e.Graphics.DrawLine(SystemPens.ControlDark, new Point(0, DEF_HEADER_HEIGHT), end);
                    end.X += (int)SelectedItem.StripRect.Width + 10;
                    e.Graphics.DrawLine(SystemPens.ControlDark, end, new Point(ClientRectangle.Width, DEF_HEADER_HEIGHT));
                }
            }
            else
            {
                if (Items.DrawnCount == 0 || Items.VisibleCount == 0)
                {
                    e.Graphics.DrawLine(SystemPens.ControlDark, new Point(0, DEF_HEADER_HEIGHT),
                                        new Point(ClientRectangle.Width, DEF_HEADER_HEIGHT));
                }
                else if (SelectedItem != null && SelectedItem.IsDrawn)
                {
                    Point end = new Point((int)SelectedItem.StripRect.Left, DEF_HEADER_HEIGHT);
                    e.Graphics.DrawLine(SystemPens.ControlDark, new Point(0, DEF_HEADER_HEIGHT), end);
                    end.X += (int)SelectedItem.StripRect.Width + 20;
                    e.Graphics.DrawLine(SystemPens.ControlDark, end, new Point(ClientRectangle.Width, DEF_HEADER_HEIGHT));
                }
            }
            #endregion

            #region Draw Menu and Close Glyphs
            if (AlwaysShowMenuGlyph || Items.DrawnCount > Items.VisibleCount)
                menuGlyph.DrawGlyph(e.Graphics);

            if (AlwaysShowClose || (SelectedItem != null && SelectedItem.CanClose))
                closeButton.DrawCross(e.Graphics);
            #endregion
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            HitTestResult result = HitTest(e.Location);
            if (result == HitTestResult.MenuGlyph)
            {
                HandledEventArgs args = new HandledEventArgs(false);
                OnMenuItemsLoading(args);

                if (!args.Handled)
                    OnMenuItemsLoad(EventArgs.Empty);

                ShowMenu();
            }
            else if (result == HitTestResult.CloseButton)
            {
                if (SelectedItem != null)
                {
                    TabStripItemClosingEventArgs args = new TabStripItemClosingEventArgs(SelectedItem);
                    OnTabStripItemClosing(args);
                    if (!args.Cancel && SelectedItem.CanClose)
                    {
                        RemoveTab(SelectedItem);
                        OnTabStripItemClosed(EventArgs.Empty);
                    }
                }
            }
            else if (result == HitTestResult.TabItem)
            {
                TabStripItem item = GetTabItemByPoint(e.Location);
                if (item != null)
                {
                    SelectedItem = item;
                    if (SelectedItemChanged != null)
                        SelectedItemChanged(new TabStripItemMouseEventArgs(item));

                    // Update the currently shown controls.
                    foreach (Control control in item.Controls)
                        control.Show();

                    // Somehow the right click doesn't work
                    // if we don't pass the thread to another
                    // function.
                    RightClick(e);
                }
            }

            Invalidate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
			CheckHot();

            if (menuGlyph.Bounds.Contains(e.Location) == true)
            {
                menuGlyph.IsMouseOver = true;
                Invalidate(menuGlyph.Bounds);
            }
            else
            {
                if (menuGlyph.IsMouseOver == true && 
                    menuOpen == false)
                {
                    menuGlyph.IsMouseOver = false;
                    Invalidate(menuGlyph.Bounds);
                }
            }

            if (closeButton.Bounds.Contains(e.Location) == true)
            {
                closeButton.IsMouseOver = true;
                Invalidate(closeButton.Bounds);
            }
            else
            {
                if (closeButton.IsMouseOver == true)
                {
                    closeButton.IsMouseOver = false;
                    Invalidate(closeButton.Bounds);
                }
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            menuGlyph.IsMouseOver = false;
            Invalidate(menuGlyph.Bounds);

            closeButton.IsMouseOver = false;
            Invalidate(closeButton.Bounds);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (isIniting)
                return;

            UpdateLayout();
        }
        #endregion

        #region Private
        #region Non-drawing Methods
        private void RightClick(MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.Right:
                    if (rightClickMenu == null) { return; }

                    Point point = new Point(e.X, e.Y);
                    rightClickMenu.Show(this, point);

                    break;
                case MouseButtons.Left:
                    break;
            }
        }

        private bool AllowDraw(TabStripItem item)
        {
            bool result = true;

            if (RightToLeft == RightToLeft.No)
            {
                if (item.StripRect.Right >= stripButtonRect.Width)
                    result = false;
            }
            else
            {
                if (item.StripRect.Left <= stripButtonRect.Left)
                    return false;
            }

            return result;
        }

        private void SetDefaultSelected()
        {
            if (selectedItem == null && Items.Count > 0)
                SelectedItem = Items[0];

            for (int i = 0; i < Items.Count; i++)
            {
                TabStripItem itm = Items[i];
                itm.Dock = DockStyle.Fill;
            }
        }

        private void OnMenuItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            TabStripItem clickedItem = (TabStripItem)e.ClickedItem.Tag;
            SelectedItem = clickedItem;

            if (SelectedItemChanged != null)
                SelectedItemChanged(new TabStripItemMouseEventArgs(clickedItem));

            // Update the currently shown controls.
            foreach (Control control in clickedItem.Controls)
                control.Show();
        }

        private void OnMenuVisibleChanged(object sender, EventArgs e)
        {
            if (menu.Visible == false)
            {
                menuOpen = false;
            }
        }

        private void OnCollectionChanged(object sender, CollectionChangeEventArgs e)
        {
            TabStripItem itm = (TabStripItem)e.Element;

            if (e.Action == CollectionChangeAction.Add)
            {
                Controls.Add(itm);
                OnTabStripItemChanged(new TabStripItemChangedEventArgs(itm, TabStripItemChangeTypes.Added));
            }
            else if (e.Action == CollectionChangeAction.Remove)
            {
                Controls.Remove(itm);
                OnTabStripItemChanged(new TabStripItemChangedEventArgs(itm, TabStripItemChangeTypes.Removed));
            }
            else
            {
                OnTabStripItemChanged(new TabStripItemChangedEventArgs(itm, TabStripItemChangeTypes.Changed));
            }

            UpdateLayout();
            Invalidate();
        }
        #endregion

        private void OnCalcTabPage(Graphics g, TabStripItem currentItem)
        {
            Font currentFont = Font;
            if (currentItem == SelectedItem)
                currentFont = new Font(Font, FontStyle.Bold);

            SizeF textSize = g.MeasureString(currentItem.Title, currentFont, new SizeF(200, 10), sf);
            textSize.Width += 20;

            if (RightToLeft == RightToLeft.No)
            {
                RectangleF buttonRect = new RectangleF(DEF_START_POS, 3, textSize.Width, 17);
                currentItem.StripRect = buttonRect;
                DEF_START_POS += (int)textSize.Width;
            }
            else
            {
                RectangleF buttonRect = new RectangleF(DEF_START_POS - textSize.Width + 1, 3, textSize.Width - 1, 17);
                currentItem.StripRect = buttonRect;
                DEF_START_POS -= (int)textSize.Width;
            }
        }

        private void OnDrawTabPage(Graphics g, TabStripItem currentItem)
        {
            bool isFirstTab = Items.IndexOf(currentItem) == 0;
            Font currentFont = Font;

            if (currentItem == SelectedItem)
                currentFont = new Font(Font, FontStyle.Bold);

            SizeF textSize = g.MeasureString(currentItem.Title, currentFont, new SizeF(200, 10), sf);
            textSize.Width += 20;
            RectangleF buttonRect = currentItem.StripRect;

            GraphicsPath path = new GraphicsPath();
            LinearGradientBrush brush;
            int mtop = 3;

            #region Draw Not Right-To-Left Tab
            if (RightToLeft == RightToLeft.No)
            {
                if (currentItem == SelectedItem || isFirstTab)
				{
					path.AddLine(buttonRect.Left - 10, buttonRect.Bottom - 1,
						buttonRect.Left + (buttonRect.Height / 2) - 3, mtop + 2);
                }
                else
                {
					path.AddLine(buttonRect.Left, buttonRect.Bottom - 1, buttonRect.Left,
						buttonRect.Bottom - (buttonRect.Height / 2) - 3);
					path.AddLine(buttonRect.Left, buttonRect.Bottom - (buttonRect.Height / 2) - 1,
						buttonRect.Left + (buttonRect.Height / 2) - 3, mtop + 2);
				}

				path.AddLine(buttonRect.Left +
					(buttonRect.Height / 2) + 2, mtop,
					buttonRect.Right - 3, mtop);
				path.AddLine(buttonRect.Right, mtop + 2,
					buttonRect.Right, buttonRect.Bottom - 1);
				path.AddLine(buttonRect.Right - 4,
					buttonRect.Bottom - 1, buttonRect.Left,
					buttonRect.Bottom - 1);
                path.CloseFigure();

                if (currentItem == currentHotItem &&
                    currentHotItem.IsHot == true)
                {
                    brush = new LinearGradientBrush(buttonRect,
                        selectedColor1, selectedColor2,
                        LinearGradientMode.Vertical);

                    g.FillPath(brush, path);
                    g.DrawPath(new Pen(selectedBorderColor), path);

                    g.DrawLine(new Pen(brush), buttonRect.Left - 9, buttonRect.Height + 2,
                            buttonRect.Left + buttonRect.Width - 1, buttonRect.Height + 2);

                    PointF textLoc = new PointF(buttonRect.Left + buttonRect.Height - 4, 
						buttonRect.Top + (buttonRect.Height / 2) - (textSize.Height / 2) 
						- 3);

                    RectangleF textRect = buttonRect;
                    textRect.Location = textLoc;
                    textRect.Width = buttonRect.Width - (textRect.Left - buttonRect.Left) - 4;
                    textRect.Height = textSize.Height + currentFont.Size / 2;

                    g.DrawString(currentHotItem.Title, currentFont, 
                        new SolidBrush(selectedForeColor), textRect, sf);
                }
                else
                {
                    if (currentItem == SelectedItem)
                    {
                        brush = new LinearGradientBrush(buttonRect,
                            selectedColor1, selectedColor2,
                            LinearGradientMode.Vertical);
                    }
                    else
                    {
                        brush = new LinearGradientBrush(buttonRect,
                            normalColor1, normalColor2,
                            LinearGradientMode.Vertical);
                    }

                    g.FillPath(brush, path);
                    if (currentItem == SelectedItem)
                        g.DrawPath(new Pen(selectedBorderColor), path);
                    else
                        g.DrawPath(new Pen(normalBorderColor), path);

                    if (currentItem == SelectedItem)
                    {
                        g.DrawLine(new Pen(brush), buttonRect.Left - 9, buttonRect.
							Height + 2, buttonRect.Left + buttonRect.Width - 1, 
							buttonRect.Height + 2);
                    }

                    PointF textLoc = new PointF(buttonRect.Left + buttonRect.Height - 4, 
						buttonRect.Top + (buttonRect.Height / 2) - (textSize.Height / 2) 
						- 3);

                    RectangleF textRect = buttonRect;
                    textRect.Location = textLoc;

					textRect.Height = textSize.Height + currentFont.Size / 2;
                    textRect.Width = buttonRect.Width - (textRect.Left - 
						buttonRect.Left) - 4;

					if (currentItem == SelectedItem)
					{
						g.DrawString(currentItem.Title, currentFont, new SolidBrush
							(selectedForeColor), textRect, sf);
					}
					else
					{
						g.DrawString(currentItem.Title, currentFont, new SolidBrush
							(normalForeColor), textRect, sf);
					}
                }
            }
            #endregion

            #region Draw Right-To-Left Tab
            if (RightToLeft == RightToLeft.Yes)
            {
				if (currentItem == SelectedItem || isFirstTab)
				{
					path.AddLine(buttonRect.Left - 10, buttonRect.Bottom - 1,
						buttonRect.Left + (buttonRect.Height / 2) - 3, mtop + 2);
				}
				else
				{
					path.AddLine(buttonRect.Left, buttonRect.Bottom - 1, buttonRect.Left,
						buttonRect.Bottom - (buttonRect.Height / 2) - 3);
					path.AddLine(buttonRect.Left, buttonRect.Bottom - (buttonRect.Height / 2) - 1,
						buttonRect.Left + (buttonRect.Height / 2) - 3, mtop + 2);
				}

				path.AddLine(buttonRect.Left +
					(buttonRect.Height / 2) + 2, mtop,
					buttonRect.Right - 3, mtop);
				path.AddLine(buttonRect.Right, mtop + 2,
					buttonRect.Right, buttonRect.Bottom - 1);
				path.AddLine(buttonRect.Right - 4,
					buttonRect.Bottom - 1, buttonRect.Left,
					buttonRect.Bottom - 1);
				path.CloseFigure();

                if (currentItem == currentHotItem &&
                    currentHotItem.IsHot == true)
                {
                    brush = new LinearGradientBrush(buttonRect,
                        selectedColor1, selectedColor2,
                        LinearGradientMode.Vertical);

                    g.FillPath(brush, path);
                    g.DrawPath(new Pen(selectedBorderColor), path);

                    g.DrawLine(new Pen(brush), buttonRect.Left - 9, buttonRect.Height + 2,
                            buttonRect.Left + buttonRect.Width - 1, buttonRect.Height + 2);

                    PointF textLoc = new PointF(buttonRect.Left + 2, buttonRect.Top + (buttonRect.Height / 2) - (textSize.Height / 2) - 2);
                    RectangleF textRect = buttonRect;
                    textRect.Location = textLoc;
                    textRect.Width = buttonRect.Width - (textRect.Left - buttonRect.Left) - 10;
                    textRect.Height = textSize.Height + currentFont.Size / 2;
                    if (hoverItem == SelectedItem)
                        textRect.Y -= 1;

                    g.DrawString(currentHotItem.Title, currentFont, 
                        new SolidBrush(ForeColor), textRect, sf);
                }
                else
                {
                    if (currentItem == SelectedItem)
                        brush = new LinearGradientBrush(buttonRect,
                            selectedColor1, selectedColor2,
                            LinearGradientMode.Vertical);
                    else
                        brush = new LinearGradientBrush(buttonRect,
                            normalColor1, normalColor2,
                            LinearGradientMode.Vertical);

                    g.FillPath(brush, path);
                    if (currentItem == SelectedItem)
                        g.DrawPath(new Pen(selectedBorderColor), path);
                    else
                        g.DrawPath(new Pen(normalBorderColor), path);

                    if (currentItem == SelectedItem)
                    {
                        g.DrawLine(new Pen(brush), buttonRect.Right + 9, buttonRect.Height + 2,
                                   buttonRect.Right - buttonRect.Width + 1, buttonRect.Height + 2);
                    }

                    PointF textLoc = new PointF(buttonRect.Left + 2, buttonRect.Top + (buttonRect.Height / 2) - (textSize.Height / 2) - 2);
                    RectangleF textRect = buttonRect;
                    textRect.Location = textLoc;
                    textRect.Width = buttonRect.Width - (textRect.Left - buttonRect.Left) - 10;
                    textRect.Height = textSize.Height + currentFont.Size / 2;

                    if (currentItem == SelectedItem)
                    {
                        textRect.Y -= 1;
                        g.DrawString(currentItem.Title, currentFont, new SolidBrush(ForeColor), textRect, sf);
                    }
                    else
                    {
                        g.DrawString(currentItem.Title, currentFont, new SolidBrush(ForeColor), textRect, sf);
                    }
                }
            }
            #endregion

            currentItem.IsDrawn = true;
        }

        private void UpdateLayout()
        {
            if (RightToLeft == RightToLeft.No)
            {
                sf.Trimming = StringTrimming.EllipsisCharacter;
                sf.FormatFlags |= StringFormatFlags.NoWrap;
                sf.FormatFlags &= StringFormatFlags.DirectionRightToLeft;

                stripButtonRect = new Rectangle(0, 0, ClientSize.Width - DEF_GLYPH_WIDTH - 2, 10);
                menuGlyph.Bounds = new Rectangle(ClientSize.Width - DEF_GLYPH_WIDTH, 2, 16, 16);
                closeButton.Bounds = new Rectangle(ClientSize.Width - 20, 2, 16, 16);
            }
            else
            {
                sf.Trimming = StringTrimming.EllipsisCharacter;
                sf.FormatFlags |= StringFormatFlags.NoWrap;
                sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;

                stripButtonRect = new Rectangle(DEF_GLYPH_WIDTH + 2, 0, ClientSize.Width - DEF_GLYPH_WIDTH - 15, 10);
                closeButton.Bounds = new Rectangle(4, 2, 16, 16);
                menuGlyph.Bounds = new Rectangle(20 + 4, 2, 16, 16);
            }

            DockPadding.Top = DEF_HEADER_HEIGHT + 1;
            DockPadding.Bottom = 1;
            DockPadding.Right = 1;
            DockPadding.Left = 1;
        }

        #region Tab EventHandlers
        private void OnTabMouseEnter(TabStripItemMouseEventArgs e)
        { Invalidate(); }

        private void OnTabMouseLeave(TabStripItemMouseEventArgs e)
        { Invalidate(); }
        #endregion
        #endregion
        #endregion

        #region ShouldSerialize
        public bool ShouldSerializeFont()
        {
            return Font != null && !Font.Equals(defaultFont);
        }

        public bool ShouldSerializeSelectedItem()
        {
            return true;
        }

        public bool ShouldSerializeItems()
        {
            return items.Count > 0;
        }

        public new void ResetFont()
        {
            Font = defaultFont;
        }
        #endregion

        #region ISupportInitialize Members
        public void BeginInit()
        {
            isIniting = true;
        }

        public void EndInit()
        {
            isIniting = false;
        }
        #endregion

        #region IDisposable
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                items.CollectionChanged -= new CollectionChangeEventHandler(OnCollectionChanged);
                menu.ItemClicked -= new ToolStripItemClickedEventHandler(OnMenuItemClicked);
                menu.VisibleChanged -= new EventHandler(OnMenuVisibleChanged);

                foreach (TabStripItem item in items)
                {
                    if (item != null && !item.IsDisposed)
                        item.Dispose();
                }

                if (menu != null && !menu.IsDisposed)
                    menu.Dispose();

                if (sf != null)
                    sf.Dispose();
            }

            base.Dispose(disposing);
        }
        #endregion

		/// <summary>
		/// Initializes the TabStrip.
		/// </summary>
		public TabStrip()
		{
			BeginInit();

			SetStyle(ControlStyles.ContainerControl, true);
			SetStyle(ControlStyles.UserPaint, true);
			SetStyle(ControlStyles.ResizeRedraw, true);
			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
			SetStyle(ControlStyles.Selectable, true);

			items = new TabStripItemCollection();
			items.CollectionChanged += new CollectionChangeEventHandler(OnCollectionChanged);
			base.Size = new Size(350, 200);

			menu = new ContextMenuStrip();
			menu.Renderer = ToolStripRenderer;
			menu.ItemClicked += new ToolStripItemClickedEventHandler(OnMenuItemClicked);
			menu.VisibleChanged += new EventHandler(OnMenuVisibleChanged);

			menuGlyph = new TabStripMenuGlyph(ToolStripRenderer);
			closeButton = new TabStripCloseButton(ToolStripRenderer);
			Font = defaultFont;
			sf = new StringFormat();

			normalColor1 = Color.FromArgb(227, 239, 255);
			normalColor2 = Color.FromArgb(152, 186, 230);
			normalBorderColor = normalColor2;

			selectedColor1 = Color.FromArgb(255, 255, 255);
			selectedColor2 = Color.FromArgb(111, 157, 217);
			selectedBorderColor = normalColor2;

			selectedForeColor = Color.Black;
			normalForeColor = Color.Black;

			EndInit();

			UpdateLayout();
		}
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)



Comments and Discussions