How do I merge and center columnheaders in datagridview in winforms application?

How can I make a columnheader in datagridview winforms merge and center like below so that it will look like "colspan = 2" in a table in html?

      Title 1            Title2      
col1      col2      col3      col4      col5
a      b      c      d      e
a      b      c      d      e
a      b      c      d      e
a      b      c      d      e

Title1 will be merged and centered  and will be the title of col2 and col3 and Title2 for the col4 and col5.

Can this be done? If no, are there any workaround I can do, and if yes, how do I do this?
stylelyfAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Bob LearnedCommented:
The DataGridView control is a beast, and I don't believe that it can do what you want out of the box.  I can't think of any workaround either.

You might be able to find a custom control that extends the DataGridView, and provides merged cells.

Discussion:

https://social.msdn.microsoft.com/Forums/en-US/933e2dd1-4c2d-4224-b4a5-426cd0ad916d/merge-cells-in-datagridview-in-windows-forms-vbnet?forum=winforms

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Jacques Bourgeois (James Burger)PresidentCommented:
This cannot be done in the DataGridView itself, as Bob hinted.

But you could use an old trick that sometimes comes handy when you need a control to display something in a way that it is not able to do so: overlay another control over it.

You could thus put other controls such as labels over the headers to simulate them. If the form and/or the columns are not sizable, you can do it directly in the Form Designer. Otherwise, you will have to react to the events that are triggered on a resize and calculate the position and size of the overlaying controls in order for them to adjust to the resize.
ArkCommented:
    Private Class TableData
        Public Property col1 As String
        Public Property col2 As String
        Public Property col3 As String
        Public Property col4 As String
        Public Property col5 As String
    End Class

    Private tData As List(Of TableData)
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        tData = New List(Of TableData)
        With DataGridView1
            .AutoGenerateColumns = False
            .Columns.Add("Title1", "Col1")
            .Columns.Add("Title1", "Col2")
            .Columns.Add("Title2", "Col3")
            .Columns.Add("Title2", "Col4")
            .Columns.Add("Title2", "Col5")
            For i = 1 To 5
                tData.Add(New TableData With {.col1 = "a", .col2 = "b", .col3 = "c", .col4 = "d", .col5 = "e"})
                .Columns(i - 1).DataPropertyName = "col" & i
            Next
            .DataSource = tData
            .ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing
            .ColumnHeadersHeight *= 2
            .ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.BottomCenter
        End With
    End Sub

    Private Sub DataGridView1_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
        If e.RowIndex = -1 AndAlso e.ColumnIndex > -1 Then
            Dim r = e.CellBounds
            r.Y += e.CellBounds.Height / 2
            r.Height = e.CellBounds.Height / 2
            e.PaintBackground(r, True)
            e.PaintContent(r)
            e.Handled = True
        End If
    End Sub

    Private Sub DataGridView1_ColumnWidthChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewColumnEventArgs) Handles DataGridView1.ColumnWidthChanged
        RefreshTop()
    End Sub

    Private Sub DataGridView1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) Handles DataGridView1.Scroll
        RefreshTop()
    End Sub

    Private Sub RefreshTop()
        With DataGridView1
            Dim rtTop = .DisplayRectangle
            rtTop.Height = .ColumnHeadersHeight / 2
            .Invalidate(rtTop)
        End With
    End Sub

    Private Sub DataGridView1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles DataGridView1.Paint
        With DataGridView1
            Dim colName = .Columns(0).Name, w As Integer
            Dim r = .GetCellDisplayRectangle(0, -1, True)
            Dim x = r.Left
            For Each c As DataGridViewColumn In .Columns
                r = DataGridView1.GetCellDisplayRectangle(c.Index, -1, True)
                If c.Name = colName Then
                    w += r.Width
                Else
                    DrawTitle(e.Graphics, r, w, x, colName)
                    colName = c.Name
                    x = r.Left
                    w = r.Width
                End If
            Next
            DrawTitle(e.Graphics, r, w, x, colName)
        End With
    End Sub

    Private Sub DrawTitle(g As Graphics, r As Rectangle, w As Integer, x As Integer, title As String)
        With DataGridView1
            r.X = x + 1
            r.Y += 1
            r.Width = w - 2
            r.Height = r.Height / 2 - 2
            g.FillRectangle(New SolidBrush(.ColumnHeadersDefaultCellStyle.BackColor), r)
            Dim format As New StringFormat()
            format.Alignment = StringAlignment.Center
            format.LineAlignment = StringAlignment.Center
            g.DrawString(title, .ColumnHeadersDefaultCellStyle.Font, New SolidBrush(.ColumnHeadersDefaultCellStyle.ForeColor), r, format)
            g.DrawLine(Pens.Black, r.Left, r.Bottom, r.Right, r.Bottom)
        End With
    End Sub

Open in new window

ArkCommented:
Oops, sorry, didn't recognize this is C# corner :(
Here is C# code
Classes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;

namespace MultiHeaderDGV
{
    public class MultiHeader
    {
        public string Text { get; set; }
        public int FirstColumn { get; set; }
        public int LastColumn { get; set; }
        public int Level { get; set; }
        public Color BackColor { get; set; }
        public Color ForeColor { get; set; }
        public Font Font { get; set; }
    }

    public class MultiHeaderRenderer
    {
        private List<MultiHeader> headers;
        private int levels;

        private int hdrHeight;
        public MultiHeaderRenderer(DataGridView dgv, List<MultiHeader> topHeaders)
        {
            headers = topHeaders;
            levels = (from h in topHeaders select h.Level).Max() + 2;
            hdrHeight = dgv.ColumnHeadersHeight;
            dgv.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;
            dgv.ColumnHeadersHeight = hdrHeight * levels;
            dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.BottomCenter;
            dgv.ColumnHeadersDefaultCellStyle.WrapMode = DataGridViewTriState.False;
            dgv.Paint += DGV_Paint;
            dgv.Scroll += DGV_Scroll;
            dgv.ColumnWidthChanged += DGV_ColumnWidthChanged;
        }

        private void DGV_ColumnWidthChanged(object sender, System.Windows.Forms.DataGridViewColumnEventArgs e)
        {
            RefreshTop((DataGridView)sender);
        }

        private void DGV_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
        {
            RefreshTop((DataGridView)sender);
        }

        private void RefreshTop(DataGridView dgv)
        {
            var rtTop = dgv.DisplayRectangle;
            rtTop.Height = hdrHeight * (levels - 1);
            dgv.Invalidate(rtTop, true);
        }

        private void DGV_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            var dgv = (DataGridView)sender;
            foreach (MultiHeader h in headers)
            {
                int firstIdx = dgv.FirstDisplayedCell.ColumnIndex;
                if (firstIdx > h.LastColumn) continue;
                int idx = Math.Max(firstIdx, h.FirstColumn);
                Rectangle cellRect = dgv.GetCellDisplayRectangle(idx, -1, true);
                int w = 0;
                for (int i = idx; i <= h.LastColumn; i++)
                {
                    w += dgv.GetCellDisplayRectangle(i, -1, true).Width;
                }
                Rectangle rc = new Rectangle(cellRect.Left + 1, h.Level * hdrHeight + 1, w - 2, hdrHeight - 2);
                var hdrStyle = dgv.ColumnHeadersDefaultCellStyle;
                Color clr = h.ForeColor.IsEmpty ? hdrStyle.ForeColor : h.ForeColor;
                Color bkClr = h.BackColor.IsEmpty ? hdrStyle.BackColor : h.BackColor;
                Font fnt = h.Font == null ? hdrStyle.Font : h.Font;
                StringFormat format = new StringFormat
                {
                    Alignment = StringAlignment.Center,
                    LineAlignment = StringAlignment.Center
                };
                e.Graphics.FillRectangle(new SolidBrush(bkClr), rc);
                e.Graphics.DrawString(h.Text, fnt, new SolidBrush(clr), rc, format);
                rc.Inflate(0, 1);
                e.Graphics.DrawRectangle(Pens.Black, rc);
            }
        }

    }
}

Open in new window

Using:
namespace MultiHeaderDGV
{
    public partial class Form1 : Form
    {
        private class TableData
        {
            public string col1 { get; set; }
            public string col2 { get; set; }
            public string col3 { get; set; }
            public string col4 { get; set; }
            public string col5 { get; set; }
            public string col6 { get; set; }
            public string col7 { get; set; }
            public string col8 { get; set; }
            public string col9 { get; set; }
        }

        public Form1()
        {
            InitializeComponent();
        }
        
        private List<TableData> tData;

        private void Form1_Load(object sender, EventArgs e)
        {
            tData = new List<TableData>();
            for (int i = 1; i <= 5; i++)
            {
                tData.Add(new TableData
                {
                    col1 = "a", col2 = "b",
                    col3 = "c", col4 = "d",
                    col5 = "e", col6 = "f",
                    col7 = "g", col8 = "h", col9 = "i"
                });
            }
            dataGridView1.DataSource = tData;
            List<MultiHeader> headers = new List<MultiHeader>();
            headers.Add(new MultiHeader
            {
                Text = "Title1",
                FirstColumn = 0, LastColumn = 3,
                Level = 0, 
                BackColor = Color.Red
            });
            headers.Add(new MultiHeader
            {
                Text = "Title2",
                FirstColumn = 4, LastColumn = 8,
                Level = 0,
                BackColor = Color.Blue,
                ForeColor = Color.White
            });
            headers.Add(new MultiHeader
            {
                Text = "SubTitle1",
                FirstColumn = 0,
                LastColumn = 1,
                Level = 1,
                BackColor = Color.Yellow
            });
            headers.Add(new MultiHeader
            {
                Text = "SubTitle2",
                FirstColumn = 2,
                LastColumn = 3,
                Level = 1,
                BackColor = Color.Yellow
            });
            headers.Add(new MultiHeader
            {
                Text = "SubTitle3",
                FirstColumn = 4,
                LastColumn = 5,
                Level = 1,
                BackColor = Color.Silver
            });
            headers.Add(new MultiHeader
            {
                Text = "SubTitle4",
                FirstColumn = 6,
                LastColumn = 8,
                Level = 1,
                BackColor = Color.Silver
            });
            MultiHeaderRenderer x = new MultiHeaderRenderer(dataGridView1, headers);
        }
    }
}

Open in new window

It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.