Vertical Tab Control with horizontal text in Winforms

45,940

Solution 1

I don't know how robust this is and I can't claim to have created it but... http://www.dreamincode.net/forums/topic/125792-how-to-make-vertical-tabs/

Here's a way of doing it.

So first we are going to change its alignment to Left, by setting the property:

Alignment = Left

If you have XP themes turned on then you may notice the weird layout of Tab Control. Don't worry we will make it fine.

As you may have noticed that Tabs are vertical, and our requirement is horizontal. So we can change the size of Tabs. But before we can do this we have to set the SizeMode property as,

SizeMode = Fixed

Now we can change the size by using the ItemSize property,

ItemSize = 30, 120 Width = 30 and Height = 120

After setting the Alignment = Left, Tab control rotates the Tabs which causes the Width and Height seem to be reversed. That is why when we increase Height, we see that width is increasing and when we increase width the height is effected.

Now Text will also be displaying, but vertically. Unfortunately there is no simple way to resolve this issue. For this purpose we have to write the Text by ourselves. To do this we will first set the DrawMode

DrawMode = OwnerDrawFixed

01

Private Sub TabControl1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles TabControl1.DrawItem
    Dim g As Graphics
    Dim sText As String

    Dim iX As Integer
    Dim iY As Integer
    Dim sizeText As SizeF

    Dim ctlTab As TabControl

    ctlTab = CType(sender, TabControl)

    g = e.Graphics

    sText = ctlTab.TabPages(e.Index).Text
    sizeText = g.MeasureString(sText, ctlTab.Font)

    iX = e.Bounds.Left + 6
    iY = e.Bounds.Top + (e.Bounds.Height - sizeText.Height) / 2

    g.DrawString(sText, ctlTab.Font, Brushes.Black, iX, iY)
End Sub

Solution 2

This is the code for a custom tab control that I'm quite fond of. You will need to copy and paste this code into a new class then rebuild the project. You will see a new custom user control displayed in your toolbox.

Vertical Tab Control with Indicator and ImageList

    Imports System.Drawing.Drawing2D
Class DotNetBarTabcontrol
    Inherits TabControl

    Sub New()
        SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint Or ControlStyles.DoubleBuffer, True)
        DoubleBuffered = True
        SizeMode = TabSizeMode.Fixed
        ItemSize = New Size(44, 136)
    End Sub
    Protected Overrides Sub CreateHandle()
        MyBase.CreateHandle()
        Alignment = TabAlignment.Left
    End Sub

    Function ToPen(ByVal color As Color) As Pen
        Return New Pen(color)
    End Function

    Function ToBrush(ByVal color As Color) As Brush
        Return New SolidBrush(color)
    End Function

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        Dim B As New Bitmap(Width, Height)
        Dim G As Graphics = Graphics.FromImage(B)
        Try : SelectedTab.BackColor = Color.White : Catch : End Try
        G.Clear(Color.White)
        G.FillRectangle(New SolidBrush(Color.FromArgb(246, 248, 252)), New Rectangle(0, 0, ItemSize.Height + 4, Height))
        'G.DrawLine(New Pen(Color.FromArgb(170, 187, 204)), New Point(Width - 1, 0), New Point(Width - 1, Height - 1))    'comment out to get rid of the borders
        'G.DrawLine(New Pen(Color.FromArgb(170, 187, 204)), New Point(ItemSize.Height + 1, 0), New Point(Width - 1, 0))                   'comment out to get rid of the borders
        'G.DrawLine(New Pen(Color.FromArgb(170, 187, 204)), New Point(ItemSize.Height + 3, Height - 1), New Point(Width - 1, Height - 1)) 'comment out to get rid of the borders
        G.DrawLine(New Pen(Color.FromArgb(170, 187, 204)), New Point(ItemSize.Height + 3, 0), New Point(ItemSize.Height + 3, 999))
        For i = 0 To TabCount - 1
            If i = SelectedIndex Then
                Dim x2 As Rectangle = New Rectangle(New Point(GetTabRect(i).Location.X - 2, GetTabRect(i).Location.Y - 2), New Size(GetTabRect(i).Width + 3, GetTabRect(i).Height - 1))
                Dim myBlend As New ColorBlend()
                myBlend.Colors = {Color.FromArgb(232, 232, 240), Color.FromArgb(232, 232, 240), Color.FromArgb(232, 232, 240)}
                myBlend.Positions = {0.0F, 0.5F, 1.0F}
                Dim lgBrush As New LinearGradientBrush(x2, Color.Black, Color.Black, 90.0F)
                lgBrush.InterpolationColors = myBlend
                G.FillRectangle(lgBrush, x2)
                G.DrawRectangle(New Pen(Color.FromArgb(170, 187, 204)), x2)


                G.SmoothingMode = SmoothingMode.HighQuality
                Dim p() As Point = {New Point(ItemSize.Height - 3, GetTabRect(i).Location.Y + 20), New Point(ItemSize.Height + 4, GetTabRect(i).Location.Y + 14), New Point(ItemSize.Height + 4, GetTabRect(i).Location.Y + 27)}
                G.FillPolygon(Brushes.White, p)
                G.DrawPolygon(New Pen(Color.FromArgb(170, 187, 204)), p)

                If ImageList IsNot Nothing Then
                    Try
                        If ImageList.Images(TabPages(i).ImageIndex) IsNot Nothing Then

                            G.DrawImage(ImageList.Images(TabPages(i).ImageIndex), New Point(x2.Location.X + 8, x2.Location.Y + 6))
                            G.DrawString("      " & TabPages(i).Text, Font, Brushes.DimGray, x2, New StringFormat With {.LineAlignment = StringAlignment.Center, .Alignment = StringAlignment.Center})
                        Else
                            G.DrawString(TabPages(i).Text, New Font(Font.FontFamily, Font.Size, FontStyle.Bold), Brushes.DimGray, x2, New StringFormat With {.LineAlignment = StringAlignment.Center, .Alignment = StringAlignment.Center})
                        End If
                    Catch ex As Exception
                        G.DrawString(TabPages(i).Text, New Font(Font.FontFamily, Font.Size, FontStyle.Bold), Brushes.DimGray, x2, New StringFormat With {.LineAlignment = StringAlignment.Center, .Alignment = StringAlignment.Center})
                    End Try
                Else
                    G.DrawString(TabPages(i).Text, New Font(Font.FontFamily, Font.Size, FontStyle.Bold), Brushes.DimGray, x2, New StringFormat With {.LineAlignment = StringAlignment.Center, .Alignment = StringAlignment.Center})
                End If

                G.DrawLine(New Pen(Color.FromArgb(200, 200, 250)), New Point(x2.Location.X - 1, x2.Location.Y - 1), New Point(x2.Location.X, x2.Location.Y))
                G.DrawLine(New Pen(Color.FromArgb(200, 200, 250)), New Point(x2.Location.X - 1, x2.Bottom - 1), New Point(x2.Location.X, x2.Bottom))
            Else
                Dim x2 As Rectangle = New Rectangle(New Point(GetTabRect(i).Location.X - 2, GetTabRect(i).Location.Y - 2), New Size(GetTabRect(i).Width + 3, GetTabRect(i).Height + 1))
                G.FillRectangle(New SolidBrush(Color.FromArgb(246, 248, 252)), x2)
                G.DrawLine(New Pen(Color.FromArgb(170, 187, 204)), New Point(x2.Right, x2.Top), New Point(x2.Right, x2.Bottom))
                If ImageList IsNot Nothing Then
                    Try
                        If ImageList.Images(TabPages(i).ImageIndex) IsNot Nothing Then
                            G.DrawImage(ImageList.Images(TabPages(i).ImageIndex), New Point(x2.Location.X + 8, x2.Location.Y + 6))
                            G.DrawString("      " & TabPages(i).Text, Font, Brushes.DimGray, x2, New StringFormat With {.LineAlignment = StringAlignment.Center, .Alignment = StringAlignment.Center})
                        Else
                            G.DrawString(TabPages(i).Text, Font, Brushes.DimGray, x2, New StringFormat With {.LineAlignment = StringAlignment.Center, .Alignment = StringAlignment.Center})
                        End If
                    Catch ex As Exception
                        G.DrawString(TabPages(i).Text, Font, Brushes.DimGray, x2, New StringFormat With {.LineAlignment = StringAlignment.Center, .Alignment = StringAlignment.Center})
                    End Try
                Else
                    G.DrawString(TabPages(i).Text, Font, Brushes.DimGray, x2, New StringFormat With {.LineAlignment = StringAlignment.Center, .Alignment = StringAlignment.Center})
                End If
            End If
        Next

        e.Graphics.DrawImage(B.Clone, 0, 0)
        G.Dispose() : B.Dispose()
    End Sub
End Class

Solution 3

This is an old question but I found a good looking C# class. After adding the normal WinForms tab control. Add this to your project as a class E.G DotNetBarTabControl.cs Then make sure to replace the Winforms tab controls to this class.

  1. this.tabControl1 = new System.Windows.Forms.TabControl();

    must be changed to:

    this.tabControl1 = new TabControls.DotNetBarTabControl();


  1. private System.Windows.Forms.TabControl tabControl1;

    must be changed to:

    private TabControls.DotNetBarTabControl tabControl1;


DotNetBarTabControl.cs Class:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

// thanks to Mavamaarten~ for coding this

namespace TabControls
{
    internal class DotNetBarTabControl : TabControl
    {
        public DotNetBarTabControl()
        {
            SetStyle(
                ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
                ControlStyles.DoubleBuffer, true);
            SizeMode = TabSizeMode.Fixed;
            ItemSize = new Size(44, 136);
            Alignment = TabAlignment.Left;
            SelectedIndex = 0;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            Bitmap b = new Bitmap(Width, Height);
            Graphics g = Graphics.FromImage(b);
            if (!DesignMode)
                SelectedTab.BackColor = SystemColors.Control;
            g.Clear(SystemColors.Control);
            g.FillRectangle(new SolidBrush(Color.FromArgb(246, 248, 252)),
                new Rectangle(0, 0, ItemSize.Height + 4, Height));
            g.DrawLine(new Pen(Color.FromArgb(170, 187, 204)), new Point(ItemSize.Height + 3, 0),
                new Point(ItemSize.Height + 3, 999));
            g.DrawLine(new Pen(Color.FromArgb(170, 187, 204)), new Point(0, Size.Height - 1),
                new Point(Width + 3, Size.Height - 1));
            for (int i = 0; i <= TabCount - 1; i++)
            {
                if (i == SelectedIndex)
                {
                    Rectangle x2 = new Rectangle(new Point(GetTabRect(i).Location.X - 2, GetTabRect(i).Location.Y - 2),
                        new Size(GetTabRect(i).Width + 3, GetTabRect(i).Height - 1));
                    ColorBlend myBlend = new ColorBlend();
                    myBlend.Colors = new Color[] { Color.FromArgb(232, 232, 240), Color.FromArgb(232, 232, 240), Color.FromArgb(232, 232, 240) };
                    myBlend.Positions = new float[] { 0f, 0.5f, 1f };
                    LinearGradientBrush lgBrush = new LinearGradientBrush(x2, Color.Black, Color.Black, 90f);
                    lgBrush.InterpolationColors = myBlend;
                    g.FillRectangle(lgBrush, x2);
                    g.DrawRectangle(new Pen(Color.FromArgb(170, 187, 204)), x2);

                    g.SmoothingMode = SmoothingMode.HighQuality;
                    Point[] p =
                    {
                        new Point(ItemSize.Height - 3, GetTabRect(i).Location.Y + 20),
                        new Point(ItemSize.Height + 4, GetTabRect(i).Location.Y + 14),
                        new Point(ItemSize.Height + 4, GetTabRect(i).Location.Y + 27)
                    };
                    g.FillPolygon(SystemBrushes.Control, p);
                    g.DrawPolygon(new Pen(Color.FromArgb(170, 187, 204)), p);

                    if (ImageList != null)
                    {
                        try
                        {
                            g.DrawImage(ImageList.Images[TabPages[i].ImageIndex],
                                new Point(x2.Location.X + 8, x2.Location.Y + 6));
                            g.DrawString("  " + TabPages[i].Text, Font, Brushes.Black, x2, new StringFormat
                            {
                                LineAlignment = StringAlignment.Center,
                                Alignment = StringAlignment.Center
                            });
                        }
                        catch (Exception)
                        {
                            g.DrawString(TabPages[i].Text, new Font(Font.FontFamily, Font.Size, FontStyle.Bold),
                                Brushes.Black, x2, new StringFormat
                                {
                                    LineAlignment = StringAlignment.Center,
                                    Alignment = StringAlignment.Center
                                });
                        }
                    }
                    else
                    {
                        g.DrawString(TabPages[i].Text, new Font(Font.FontFamily, Font.Size, FontStyle.Bold),
                            Brushes.Black, x2, new StringFormat
                            {
                                LineAlignment = StringAlignment.Center,
                                Alignment = StringAlignment.Center
                            });
                    }

                    g.DrawLine(new Pen(Color.FromArgb(200, 200, 250)), new Point(x2.Location.X - 1, x2.Location.Y - 1),
                        new Point(x2.Location.X, x2.Location.Y));
                    g.DrawLine(new Pen(Color.FromArgb(200, 200, 250)), new Point(x2.Location.X - 1, x2.Bottom - 1),
                        new Point(x2.Location.X, x2.Bottom));
                }
                else
                {
                    Rectangle x2 = new Rectangle(new Point(GetTabRect(i).Location.X - 2, GetTabRect(i).Location.Y - 2),
                        new Size(GetTabRect(i).Width + 3, GetTabRect(i).Height - 1));
                    g.FillRectangle(new SolidBrush(Color.FromArgb(246, 248, 252)), x2);
                    g.DrawLine(new Pen(Color.FromArgb(170, 187, 204)), new Point(x2.Right, x2.Top),
                        new Point(x2.Right, x2.Bottom));
                    if (ImageList != null)
                    {
                        try
                        {
                            g.DrawImage(ImageList.Images[TabPages[i].ImageIndex],
                                new Point(x2.Location.X + 8, x2.Location.Y + 6));
                            g.DrawString("  " + TabPages[i].Text, Font, Brushes.DimGray, x2, new StringFormat
                            {
                                LineAlignment = StringAlignment.Center,
                                Alignment = StringAlignment.Center
                            });
                        }
                        catch (Exception)
                        {
                            g.DrawString(TabPages[i].Text, Font, Brushes.DimGray, x2, new StringFormat
                            {
                                LineAlignment = StringAlignment.Center,
                                Alignment = StringAlignment.Center
                            });
                        }
                    }
                    else
                    {
                        g.DrawString(TabPages[i].Text, Font, Brushes.DimGray, x2, new StringFormat
                        {
                            LineAlignment = StringAlignment.Center,
                            Alignment = StringAlignment.Center
                        });
                    }
                }
            }

            e.Graphics.DrawImage(b, new Point(0, 0));
            g.Dispose();
            b.Dispose();
        }
    }
}

enter image description here

You can have fun making a dark theme to. In the photo Form2 uses the material skin. It can be found on NuGet as well.

Solution 4

I have decided to share the code I developed since some people, such as Amit Andharia, would like to benefit from it.

This is the result after I had implemented Rob P.'s answer.

Vertical Tabs Control screenshot

Release Notes:

  • Full design time support
  • Automatic resizing of tabs (up to 128px wide)
  • Tab icons implemented
  • Unused properties have been hidden

The code can be downloaded from here.

Solution 5

There is a tutorial provided by Microsoft for doing this with the existing TabControl on MSDN, with sample code given in both C# and Visual Basic .NET. Their method is based on using owner drawing. Summarizing their steps below:

  1. Set the TabControl's Alignment property to Right.

  2. Ensure all tabs are the same horizontal width by setting the SizeMode property to Fixed.

  3. Set the ItemSize property to your preferred size for the tabs, keeping in mind that the width and height are reversed.

  4. Set the DrawMode property to OwnerDrawFixed.

  5. Set up an event handler for the TabControl's DrawItem event, and place your owner drawing code in there dictating how each tab should be displayed. Their C# sample code for the event handler is reproduced below for convenience (it assumes your TabControl is named tabControl1:

    private void tabControl1_DrawItem(Object sender, System.Windows.Forms.DrawItemEventArgs e)
    {
        Graphics g = e.Graphics;
        Brush _textBrush;
    
        // Get the item from the collection.
        TabPage _tabPage = tabControl1.TabPages[e.Index];
    
        // Get the real bounds for the tab rectangle.
        Rectangle _tabBounds = tabControl1.GetTabRect(e.Index);
    
        if (e.State == DrawItemState.Selected)
        {
    
            // Draw a different background color, and don't paint a focus rectangle.
            _textBrush = new SolidBrush(Color.Red);
            g.FillRectangle(Brushes.Gray, e.Bounds);
        }
        else
        {
            _textBrush = new System.Drawing.SolidBrush(e.ForeColor);
            e.DrawBackground();
        }
    
        // Use our own font.
        Font _tabFont = new Font("Arial", (float)10.0, FontStyle.Bold, GraphicsUnit.Pixel);
    
        // Draw string. Center the text.
        StringFormat _stringFlags = new StringFormat();
        _stringFlags.Alignment = StringAlignment.Center;
        _stringFlags.LineAlignment = StringAlignment.Center;
        g.DrawString(_tabPage.Text, _tabFont, _textBrush, _tabBounds, new StringFormat(_stringFlags));
    }
    

You can probably experiment with your ItemSize property and the _tabFont value from the above code to fine-tune your tabs' appearance to whatever you need. For even fancier styling, I'd recommend looking at this other MSDN article as a starting point.

(Source: How to: Display Side-Aligned Tabs with TabControl (MSDN))

Share:
45,940
Alex Essilfie
Author by

Alex Essilfie

I the team leader of a freelance software developing group mainly specialising in Desktop Application development. I taught myself programming in 2005. My primary languages are Visual Basic (VB6 &amp; VB.NET), C# and Python. Contact: email: [email protected] (Solve reCAPTCHA to view email) I am #SOreadytohelp Please start the title of your message with StackOverflow so it gets filtered and I get to you faster.

Updated on April 06, 2020

Comments

  • Alex Essilfie
    Alex Essilfie about 4 years

    I would like to have the tabs on my TabControl displayed on the left, or sometimes right.
    Unlike the System.Windows.Forms.TabControl, however, I would like the text to remain horizontal instead of being rotated by 90 or 270 degrees to the horizontal.

    Here are a couple of pictures illustrating the concept Vertical tabs in Visual Studio Vertical tabs in Firefox

    Though I could write code to do this myself in about an hour or two, I just thought I'd ask first if there is any existing Winforms control that implements such feature.

    NB: Any existing solution should preferably be non-commercial.

    Thanks.

  • Alex Essilfie
    Alex Essilfie over 12 years
    Thanks for the help. I tweaked the code a bit but it wasn't as long as it would taken to write a new control from scratch.
  • Amit Andharia
    Amit Andharia over 9 years
    @AlexEssilfie did you made it look exactly same as screenshots you've included? if so, can you share the code?
  • Alex Essilfie
    Alex Essilfie over 9 years
    @AmitAndharia: I did not desire it to be exactly as shown in the screenshots but I was able to get it to work well enough for me. I'll upload my code later today or within three days max in the interest of the wider community.
  • Amit Andharia
    Amit Andharia over 9 years
    @AlexEssilfie I'll be waiting to look at it. Actually i've an urgent assignment which requires it, if you can share today, that would be gr8 help.
  • Alex Essilfie
    Alex Essilfie over 9 years
    @AmitAndharia: I have uploaded the code. See my answer for details.
  • Amit Andharia
    Amit Andharia over 9 years
    Thanks Alex. I've been actually thinking it would be similar to the screenshot you posted and hence was interested in looking at code, regardless, code you shared is really helpful.
  • Alex Essilfie
    Alex Essilfie over 9 years
    @AmitAndharia: I wrote one that replicates the Visual Studio style but I unfortunately lost that code so I had to bug-fix this one and pack it into a .vb file for use. If you so desire, I could reimplement the VS-style one but I can't assure you of getting it very soon.
  • Amit Andharia
    Amit Andharia over 9 years
    Thanks that would be gr8 if you can do something similar and share.
  • Alex Essilfie
    Alex Essilfie almost 9 years
    @dahsra I didn't write a C# version. I'll have to work on that conversion and provide you with the link in a day or two.
  • Alex Essilfie
    Alex Essilfie about 7 years
    Did you read the answer by Rob P.? You have repeated the exact same thing almost six years after he posted his answer.
  • Knowledge Cube
    Knowledge Cube about 7 years
    @AlexEssilfie I wouldn't say exactly the same. First, my provided code is in C#, whereas his was provided in VB.NET. Second, I'm using MSDN as my source as opposed to a forum post from elsewhere, so I imagine the version of the code I'm presenting is more likely to represent best practices for approaching this. Third, there are subtle differences in the code if you care to look (using GetTabRect() instead of e.Bounds, code to account for selected states, etc.). Basically, I posted my answer because I believed the information I had to share was more complete and of higher quality.
  • J. Rodríguez
    J. Rodríguez over 5 years
    How can I activate a scrollbar when the items exceed the visible part of the screen?
  • Jorny
    Jorny almost 5 years
    How i do implement it in C#, help me please
  • Jonas
    Jonas about 4 years
    @Jorny C# example provided.
  • DrBB
    DrBB over 3 years
    There are a lot of missing Dispose() calls which need to be added. I translated this to C# and it leaked everywhere. StringFormat, Font, Pen, Brush all need to have Dispose() called.