Windows Develop Bookmark and Share   
 index > Windows Forms Data Controls and Databinding > DataGridView merging headers problems.
 

DataGridView merging headers problems.

Ok after i looked over the net, found few solutions for custom drawing merged headers, but none of it would work good enough for me.
That led me to the descision to write my own.

After some tests i get stuck to some problems, that i can't solve by myself.

Here is the custom class that should handle the paint thingies about the header.
  public partial class DGVColumnHeader : DataGridViewColumnHeaderCell
    {
        private string mergedText;
        private DGVColumnHeader leftHeader = null;
        private DGVColumnHeader rightHeader = null;
        private bool isMouseOver = false;
        private System.Drawing.Point mouseCoords;
        public string MergedText
        {
            get
            {
                return mergedText;
            }
            set
            {
                mergedText = value;
            }
        }

        public DGVColumnHeader()
        {
        }

        private int GetMostLeftHeader(DGVColumnHeader header)
        {
            if (header.leftHeader != null && header.leftHeader.MergedText == header.MergedText)
            {
                return GetMostLeftHeader(header.leftHeader);
            }
            return header.DataGridView.Columns[header.ColumnIndex].DisplayIndex;
        }

        private DataGridViewColumn GetColumn(int columnIndex)
        {
            return this.DataGridView.Columns[columnIndex];
        }

        private DataGridViewColumn GetPreviousColumn(int columnIndex)
        {
            return this.DataGridView.Columns.GetPreviousColumn(this.GetColumn(columnIndex), DataGridViewElementStates.Visible, DataGridViewElementStates.None);
        }

        private DataGridViewColumn GetNextColumn(int columnIndex)
        {
            return this.DataGridView.Columns.GetNextColumn(this.GetColumn(columnIndex), DataGridViewElementStates.Visible, DataGridViewElementStates.None);
        }

        private void SetSurroundingHeaders()
        {
            if (this.GetColumn(this.ColumnIndex).DisplayIndex > 0)
            {
                if (this.GetPreviousColumn(this.ColumnIndex).HeaderCell.GetType() == this.GetType() && ((DGVColumnHeader)this.GetPreviousColumn(this.ColumnIndex).HeaderCell).MergedText == this.MergedText)
                {
                    leftHeader = (DGVColumnHeader)this.GetPreviousColumn(this.ColumnIndex).HeaderCell;
                }
                else
                {
                    leftHeader = null;
                }
            }
            else
            {
                leftHeader = null;
            }

            if (this.GetColumn(this.ColumnIndex).DisplayIndex < this.DataGridView.Columns.Count)
            {
                if (this.GetNextColumn(this.ColumnIndex).HeaderCell.GetType() == this.GetType() && ((DGVColumnHeader)this.GetNextColumn(this.ColumnIndex).HeaderCell).MergedText == this.MergedText)
                {
                    rightHeader = (DGVColumnHeader)this.GetNextColumn(this.ColumnIndex).HeaderCell;
                }
                else
                {
                    rightHeader = null;
                }
            }
            else
            {
                rightHeader = null;
            }
        }

        private void PaintCellBorder(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, bool drawSelection)
        {
            PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);
            if ((advancedBorderStyle.Left & DataGridViewAdvancedCellBorderStyle.Outset) == DataGridViewAdvancedCellBorderStyle.Outset)
            {
                using (LinearGradientBrush brush = new LinearGradientBrush(new Point(cellBounds.Left, cellBounds.Top), new Point(cellBounds.Left, cellBounds.Bottom), Color.White, (drawSelection) ? Color.DarkCyan : Color.WhiteSmoke))
                {
                    Point location = new Point();
                    location.X = cellBounds.Left;
                    location.Y = cellBounds.Location.Y + 2;
                    Rectangle rect = cellBounds;
                    rect.Location = location;
                    rect.Width = (drawSelection) ? 2 : 1;
                    rect.Height -= 3;
                    graphics.FillRectangle(brush, rect);
                }
            }
            if ((advancedBorderStyle.Right & DataGridViewAdvancedCellBorderStyle.OutsetPartial) == DataGridViewAdvancedCellBorderStyle.OutsetPartial)
            {
                using (LinearGradientBrush brush = new LinearGradientBrush(new Point(cellBounds.Right - 2, cellBounds.Top + 2), new Point(cellBounds.Right - 1, cellBounds.Bottom), Color.WhiteSmoke, (drawSelection) ? Color.DarkCyan : Color.Gainsboro))
                {
                    Point location = new Point();
                    location.X = cellBounds.Right - 1;
                    location.Y = cellBounds.Location.Y + 2;
                    Rectangle rect = cellBounds;
                    rect.Location = location;
                    rect.Width = 1;
                    rect.Height -= 3;
                    graphics.FillRectangle(brush, rect);
                }
            }
        }

        private void PaintMergedHeader(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates dataGridViewElementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts, bool drawSelection)
        {
            using (LinearGradientBrush brush = new LinearGradientBrush(cellBounds, Color.White, cellStyle.BackColor, LinearGradientMode.Vertical))
            {
                graphics.FillRectangle(brush, cellBounds);
            }
            if (drawSelection)
            {
                using (LinearGradientBrush brush = new LinearGradientBrush(cellBounds, Color.FromArgb(127, Color.White), Color.FromArgb(127, Color.Aquamarine), LinearGradientMode.Vertical))
                {
                    graphics.FillRectangle(brush, cellBounds);
                }
            }
            using (SolidBrush brush = new SolidBrush(cellStyle.ForeColor))
            {
                using (StringFormat stringFormat = new StringFormat())
                {
                    stringFormat.Alignment = StringAlignment.Center;
                    stringFormat.LineAlignment = StringAlignment.Center;
                    graphics.DrawString(value.ToString(), cellStyle.Font, brush, new RectangleF(cellBounds.Location, cellBounds.Size), stringFormat);
                }
            }
            PaintCellBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle, drawSelection);
        }

        protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates dataGridViewElementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            DataGridViewAdvancedBorderStyle upperBorder = advancedBorderStyle;
            DataGridViewAdvancedBorderStyle lowerBorder = new DataGridViewAdvancedBorderStyle();
            lowerBorder.Right = DataGridViewAdvancedCellBorderStyle.OutsetPartial;
            lowerBorder.Left = DataGridViewAdvancedCellBorderStyle.Outset;
            lowerBorder.Top = DataGridViewAdvancedCellBorderStyle.OutsetDouble;
            lowerBorder.Bottom = DataGridViewAdvancedCellBorderStyle.Outset;

            if (mergedText != null)
            {
                SetSurroundingHeaders();

                Rectangle upperPart = cellBounds;
                upperPart.Height /= 2;
                upperPart.Height += 1;

                cellBounds.Y += cellBounds.Height / 2;
                cellBounds.Height += (cellBounds.Height % 2 == 0) ? 0 : 1;
                cellBounds.Height /= 2;




                bool upperSelection = false, lowerSelection = false;

                if (isMouseOver && upperPart.Top <= mouseCoords.Y && upperPart.Bottom >= mouseCoords.Y)
                {
                    upperSelection = true;
                }

                if (isMouseOver && cellBounds.Top <= mouseCoords.Y && cellBounds.Bottom >= mouseCoords.Y)
                {
                    lowerSelection = true;
                }





                if (leftHeader != null)
                {
                    upperBorder.Left = DataGridViewAdvancedCellBorderStyle.None;
                }
                else
                {
                    upperBorder.Left = DataGridViewAdvancedCellBorderStyle.Outset;
                }

                if (rightHeader != null)
                {
                    upperBorder.Right = DataGridViewAdvancedCellBorderStyle.None;
                }
                else
                {
                    upperBorder.Left = DataGridViewAdvancedCellBorderStyle.Outset;
                    upperBorder.Right = DataGridViewAdvancedCellBorderStyle.OutsetPartial;
                    if (this.leftHeader != null)
                    {
                        int test = this.GetMostLeftHeader(leftHeader);
                        Point newLocation = new Point();
                        Rectangle mergedHeader = new Rectangle();

                        newLocation = this.DataGridView.GetCellDisplayRectangle(this.ColumnIndex, this.RowIndex, true).Location;

                        foreach (DataGridViewColumn dgvColumn in this.DataGridView.Columns)
                        {
                            if (dgvColumn.DisplayIndex >= test & dgvColumn.DisplayIndex <= this.GetColumn(this.ColumnIndex).DisplayIndex && dgvColumn.Displayed == true)
                            {
                                if (((DGVColumnHeader)dgvColumn.HeaderCell).Equals(this)) newLocation.X -= mergedHeader.Width;
                                mergedHeader.Width += dgvColumn.Width;
                            }
                        }

                        mergedHeader.Height = upperPart.Height;
                        mergedHeader.Location = newLocation;
                        PaintMergedHeader(graphics, clipBounds, mergedHeader, rowIndex, dataGridViewElementState, mergedText, mergedText, errorText, cellStyle, upperBorder, paintParts, upperSelection);
                    }
                    else
                    {
                        PaintMergedHeader(graphics, clipBounds, upperPart, rowIndex, dataGridViewElementState, mergedText, mergedText, errorText, cellStyle, upperBorder, paintParts, upperSelection);
                    }
                }

                PaintMergedHeader(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle, lowerBorder, paintParts, lowerSelection);
            }
            else
            {
                PaintMergedHeader(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle, lowerBorder, paintParts, isMouseOver);
            }
        }

        protected override void OnMouseEnter(int rowIndex)
        {
            isMouseOver = true;
            base.OnMouseEnter(rowIndex);
        }

        protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
        {
            mouseCoords = e.Location;
            this.DataGridView.Invalidate(this.DataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false));
            base.OnMouseMove(e);
        }

        protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
        {
            base.OnMouseDown(e);
        }

        protected override void OnMouseLeave(int rowIndex)
        {
            isMouseOver = false;
            base.OnMouseLeave(rowIndex);
        }

    }
The problems are that when i move the mouse over the most right part of the merged header, it should draw transperent (selection) rectangle, but it draws it with the wrong size.

Also when i scroll left/right and the most left columns gets hidden, then i scroll back they aren't repaintet propertly.
I can override the scroll event and invalidate the needed rectangle, but that would slow the whole thing.

There are problems only with the upper part of the merged header. The lower one is drawn corectly whatever i do ...

I would realy love some help here, or some ideas. Thanks in advance.

Daniel.

Daniel Tsviatkov  Thursday, August 20, 2009 11:36 PM
Anyone please ?
Daniel Tsviatkov  Monday, August 24, 2009 2:59 PM
Hi Daniel,

Sorry for the late reply. I have tested the code and did not meet any issues. Could you please let me know the detail steps of your testing?
I have created a custom DataGridView to test, this is my code:
    public class DGVNewHeader : DataGridView
    {
        public DGVNewHeader()
        {
            this.BindingContextChanged += new EventHandler(DGVNewHeader_BindingContextChanged);
        }

        void DGVNewHeader_BindingContextChanged(object sender, EventArgs e)
        {
            foreach (DataGridViewColumn col in this.Columns)
            {
                col.HeaderCell = new DGVColumnHeader();
            }
        }
    }
This link shows the details about how to build and use a DataGridViewColumnHeaderCell:
http://msdn.microsoft.com/en-us/library/aa480727.aspx.

Let me know if this helps.
Aland Li

Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Thursday, August 27, 2009 6:58 AM

Hi,

We are changing the issue type to “General Discussion�because you have not followed up with the necessary information. If you have more time to look at the issue and provide more information, please feel free to change the issue type back to “Question�by opening the Options list at the top of the post window, and changing the type. If the issue is resolved, we will appreciate it if you can share the solution so that the answer can be found and used by other community members having similar questions.

Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Monday, August 31, 2009 2:50 AM

You can use google to search for other answers

Custom Search

More Threads

• Help with datasets
• How to return one datarow
• How can I custom the TableAdapter code generating?
• a SqlDataSource can execute two InsertCommand??
• Fill dataset through threading
• Best way to Implement
• Changing a DataGridViewButtonColumn to show an icon
• economical way of filling a listbox
• newbie: drag & drop datagridview
• Autocomplete combo in a data grid.