Share via


Change the Border of a Full Selected Row

Question

Tuesday, February 10, 2009 10:44 AM

 Hi All,

I'm trying to do the following:

I have a datagridview with a fullrowselected selection mode, and I would like to change the full row border when selected, I've tried:

Private

Sub DGCAL_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DGCAL.CellPainting

If e.RowIndex = DGCAL.CurrentRow.Index Then
  e.Paint(e.CellBounds, DataGridViewPaintParts.Border)
  Using p = New Pen(Color.Black, 4)
     Dim rect As Rectangle = e.CellBounds
     rect.Width -= 2
     rect.Height -= 2
     e.Graphics.DrawRectangle(p, rect)
  End Using
  e.Handled = True
End If

End Sub

But I get borders in all cells inside the row insted of a single border all over the row, any suggestion would be eternally appreciated.

Thanks in advance

All replies (13)

Saturday, February 21, 2009 1:01 PM ✅Answered

Hi George,

you have to handle the RowPrePaint event to prevent the grid from painting the SelectionBackColor over the row's current background.

This is the code from above, which handles this event:
(C#)

private void HandleRowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)  
{  
    DataGridViewElementStates state;  
 
    state = e.State & DataGridViewElementStates.Selected;  
 
    if (state == DataGridViewElementStates.Selected)  
    {  
        e.PaintParts &=                                                // prevent the grid from automatically      
       ~(                                                              // painting the selection background     
           DataGridViewPaintParts.Focus |  
           DataGridViewPaintParts.SelectionBackground  
        );  
    }  

(VB)

Private Sub HandleRowPrePaint(ByVal sender As Object, ByVal e As DataGridViewRowPrePaintEventArgs)  
    Dim state As DataGridViewElementStates = (e.State And DataGridViewElementStates.Selected)  
    If (state = DataGridViewElementStates.Selected) Then 
        e.PaintParts = (e.PaintParts And Not (DataGridViewPaintParts.SelectionBackground Or DataGridViewPaintParts.Focus))  
    End If 
End Sub 

I you don't handle this event, the grid will fill a rectangle with the selection background color, which will be painted over the row's current background.

Please tell me, if I'm missing something or if this doesn't solve your problem.

Hope this helps,
franking


Tuesday, February 10, 2009 12:44 PM | 1 vote

Hi,

How about handling the RowPostPaint event:

http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.rowpostpaint.aspx

The argument for this event give you the row bounds, this should allow you to draw a border around the entire row.

Regards, Colin E.

http://www.scottlogic.co.uk/blog/wpf/ - my WPF blog
http://www.codeproject.com/KB/WPF/WPFDataGridExamples.aspx - WPF DataGrid Practical Examples


Tuesday, February 10, 2009 1:26 PM | 1 vote

RowPostPaint is the right way. But you still have to prevent the grid from drawing the default selection rectangle.

Use RowPrePaint to prevent the grid from drawing the selection border/background. This can be done by setting the DataGridViewRowPrePaintEventArgs.PaintParts property.
Use RowPostPaint to draw a rectangle around the selected row.

Have a look here http://msdn.microsoft.com/en-us/library/85kxk29c.aspx

Here's a simple example based on the link from above:

using System;  
using System.Windows.Forms;  
using System.Drawing.Drawing2D;  
using System.Data;  
using System.Drawing;  
 
namespace Tester  
{  
    class CustomRowSelectionPainting:Form  
    {  
        DataGridView gridView;                                                // a gridview  
        DataTable tbl;                                                        // a table with some data  
        BindingSource bsData;                                                 // a source..  
 
        const float BORDER_WIDTH=2f;                                          // specify the width of selection border  
        int oldCellY = -1;                                                    // to invalidate the old row...  
                                                                              // don't really need this...  
 
        public CustomRowSelectionPainting()  
        {  
            gridView = new DataGridView();                                    // set up the grid and the source  
            bsData = new BindingSource();  
            tbl = new DataTable();  
 
            tbl.Columns.Add("FirstColumn", typeof(String));                   // add some columns  
            tbl.Columns.Add("SecondColumn", typeof(String));                  
 
            bsData.DataSource = tbl;                                          // connect the table  
            gridView.Dock = DockStyle.Fill;                                   // set up the source  
            gridView.DataSource = bsData;              
            gridView.DefaultCellStyle.SelectionForeColor = SystemColors.WindowText;  
            gridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;  
            gridView.CurrentCellChanged += new EventHandler(HandleCurrentCellChanged);  
            gridView.RowPostPaint += new DataGridViewRowPostPaintEventHandler(HandleRowPostPaint);  
            gridView.RowPrePaint += new DataGridViewRowPrePaintEventHandler(HandleRowPrePaint);  
 
            Controls.Add(gridView);  
        }  
 
        private void HandleCurrentCellChanged(object sender, EventArgs e)  
        {  
            if (oldCellY != -1)  
            {  
                gridView.InvalidateRow(oldCellY);  
            }  
            oldCellY = gridView.CurrentCellAddress.Y;  
        }  
 
        private void HandleRowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)  
        {  
            DataGridViewElementStates state;  
 
            state = e.State & DataGridViewElementStates.Selected;              
 
            if (state == DataGridViewElementStates.Selected)                   
            {  
                e.PaintParts &=                                                // prevent the grid from automatically   
               ~(                                                              // painting the selection background  
                   DataGridViewPaintParts.Focus |  
                   DataGridViewPaintParts.SelectionBackground  
                );  
            }  
        }  
 
        private void HandleRowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)           
        {  
            Rectangle rowRect;                                                 // a selection rectangle  
            DataGridViewElementStates state;  
 
            state = e.State & DataGridViewElementStates.Selected;              // only paint on selected row  
            if (state == DataGridViewElementStates.Selected)  
            {  
                int iBorder = Convert.ToInt32(BORDER_WIDTH);                   // calculate columns width  
                int columnsWidth = gridView.Columns.GetColumnsWidth(DataGridViewElementStates.Visible);  
                int xStart = gridView.RowHeadersWidth;  
 
                // need do calculate the clipping rectangle, because you can't use e.RowBounds  
                rowRect =  
                   new Rectangle  
                   (  
                       xStart,                                                 // start after the row header  
                       e.RowBounds.Top + iBorder - 1,                          // at the top of the row  
                       columnsWidth - gridView.HorizontalScrollingOffset + 1,  // get the visible part of the row  
                       e.RowBounds.Height - iBorder                            // get the row's height  
                    );  
 
                // draw the border  
                using (Pen pen = new Pen(gridView.DefaultCellStyle.SelectionBackColor, BORDER_WIDTH))  
                {  
                    e.Graphics.DrawRectangle(pen, rowRect);                    // you can't use e.RowBounds here!  
                }  
            }  
        }  
        [STAThread]  
        public static void Main()  
        {  
            Application.EnableVisualStyles();  
            Application.SetCompatibleTextRenderingDefault(false);  
            Application.Run(new CustomRowSelectionPainting());  
        }  
    }  
}  
 

Hope this helps,
franking


Saturday, February 14, 2009 12:25 AM

 Colin,

Thanks for your reply, I've seen the rowpostpaint event and it works, but it has some rendering problems, if I minimize the program and then maximize it shows inside the square what it was before maximizing it. Also when populating the grid the first row appears in white, I have to click another row to see the firsts' content, any ideas how to solve it ?

Thanks in advance


Saturday, February 14, 2009 12:27 AM

Franking,

Thanks for your reply, I've used your code and it works, but it has some rendering problems, if I minimize the program and then maximize it shows inside the square what it was before maximizing it. Also when populating the grid the first row appears in white, I have to click another row to see the firsts' content, any ideas how to solve it ?

Thanks in advance


Wednesday, February 18, 2009 10:20 PM

C'mon guys, can anybody  help me ?


Friday, February 20, 2009 3:45 PM

Hi George,

did you change the DefaultCellStyle.SelectionForeColor ? If you don't change this, the text insinde will be colored white while the background is white as well...So you will end up having white letters on white background.

I tried minimizing and maximizing the window and it's working without rendering problems.
Can you reproduce this with a short example?

regards,
franking


Friday, February 20, 2009 10:41 PM

Thanks Franking, here's what I'm doing:

Private

Sub DGCAL_RowPostPaint(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowPostPaintEventArgs) Handles DGCAL.RowPostPaint

If DGCAL.RowCount > 0 And rowIndexFromMouseDown > -1 Then
    If e.RowIndex = DGCAL.CurrentRow.Index Then
         Using p = New Pen(Color.Black, 4)
         Dim rect As Rectangle = DGCAL.GetRowDisplayRectangle(rowIndexFromMouseDown, True)
         rect.Width -= 3
         rect.Height -= 3
         rect.X += 2
         rect.Y += 1
         e.Graphics.DrawRectangle(p, rect)
         e.Graphics.FillRectangle(Brushes.Transparent, rect)
         End Using
     End If
End If

End Sub

I'm using on DGCAL:
DefaultCellStyle > ForeColor=Black
DefaultCellStyle > SelectionBackColor=Transparent
DefaultCellStyle > SelectionForeColor=Black

Now, the thing is that if I minimize the program (let's suppose I have in my desktop the Vista classic desktop image) when the program is maximized, I still can see the desktop image in the selectedrow area, I have to select another row to get rid of the desktop image area. Same happens when I pass a program over the selectedrow, let's say I have the program maximized and I pass the messenger or the winamp program over the selected row, the object image remains inside the row.

Another issue, I'm using datagridview to show two lines of production, the DGV starts showing line1, if I select the line 2 (which populates DGV with new values), I still see the first value of the Line 1, I have to select another row to see the first's line 2 value.

In the begginig I thought it was my computer, but I have tested it in 4 pc's already and I stll get the same behavior, that's I call "the rendering problem".

Any suggestion ?

Thanks


Saturday, February 21, 2009 12:38 AM

Hi George,

using Brushes.Transparent will have unexpectable results. The same applies for DefaultCellStyle.SelectionBackColor being set to Color.Transparent. This means, the background will show through and you can't be sure what the background will be. The background can also be the Windows Desktop. I'm sure, this causes parts of the rendering problems. Just drop those commands.

Do you handle the RowPrePaint event? You have to exclude DataGridViewPaintPart.SelectionBackground and DataGridViewPaintPart.Focus from PaintParts, otherwise the background will be painted anyway.

regards,
franking


Saturday, February 21, 2009 10:11 AM

Franking,

I really appreciate your support and time, I have dropped DefaultCellStyle.SelectionBackColor and Brushes.Transparent, it works perfectly but I have a problem, the reason why I was playing with Brushes.Transparent and DefaultCellStyle.SelectionBackColor= Transparent is because my DataGridView has rows with different back colors, these colors are set according to the value of some cell, it could be yellow, red or none and I'm also using AlternatingRowsDefaultCellstyle, if leave the datagridview like you suggested me when you select a row you can't say which color has the selected row because now every selected row's back color will be grey and that's a real problem for me.

I don't handle the RowPrePaint event, is it necessary ?

I was thinking if it's possible to catch the color of the selected row, then use some kind of a brush with alpha, or could you suggest me how to work this out ?

Thanks again for your time.


Monday, February 23, 2009 8:50 AM

Franking,

I just don't know how to thank you, you just solved it !!!!, If you were near I'd buy you some beers !!!!

I leave your solution for someone with the same problem, it works perfectly now:

Private Sub DGCAL_RowPostPaint(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowPostPaintEventArgs) Handles DGCAL.RowPostPaint  
        If DGCAL.RowCount > 0 And rowIndexFromMouseDown > -1 Then 
            If e.RowIndex = DGCAL.CurrentRow.Index Then 
                Using p = New Pen(Color.Black, 4)  
                    Dim rect As Rectangle = DGCAL.GetRowDisplayRectangle(rowIndexFromMouseDown, True)  
                    rect.Width -= 3  
                    rect.Height -= 3  
                    rect.X += 2  
                    rect.Y += 1  
                    e.Graphics.DrawRectangle(p, rect)  
                End Using  
            End If 
        End If 
    End Sub 
 
    Private Sub DGCAL_RowPrePaint(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowPrePaintEventArgs) Handles DGCAL.RowPrePaint  
        Dim state As DataGridViewElementStates = (e.State And DataGridViewElementStates.Selected)  
        If (state = DataGridViewElementStates.Selected) Then 
            e.PaintParts = (e.PaintParts And Not (DataGridViewPaintParts.SelectionBackground Or DataGridViewPaintParts.Focus))  
        End If 
    End Sub 

Thanks again !!!!!!!


Thursday, April 14, 2011 1:28 PM

This is another sample in VC++ (same as above, but look better)

 

System::Void Form1::dg_RowPrePaint(System::Object^  sender, System::Windows::Forms::DataGridViewRowPrePaintEventArgs^  e)
{
    bool    focus = false;

    if ((e->State & DataGridViewElementStates::Selected) == DataGridViewElementStates::Selected)
    {
        e->PaintParts &= ~(::DataGridViewPaintParts::Focus | ::DataGridViewPaintParts::SelectionBackground);
    }
                             
}

System::Void Form1::dg_RowPostPaint(System::Object^  sender, System::Windows::Forms::DataGridViewRowPostPaintEventArgs^  e)
{
    if (   dg->RowCount == 0
        || e->RowIndex != dg->CurrentRow->Index
        )
    {
        return;
    }

    Pen ^       pen = gcnew Pen(Color::Blue, 1.5);
    Rectangle   rect = dg->GetRowDisplayRectangle(e->RowIndex, true);
    rect.Width   = Main->Columns->GetColumnsWidth(DataGridViewElementStates::Visible)
                 - dg->HorizontalScrollingOffset - 2
                 ;
    rect.Height -= 2;
    rect.X      += 0.5;
    rect.Y      += 0.5;
    e->Graphics->DrawRectangle(pen, rect);

}


Tuesday, July 19, 2016 10:37 AM

Hello,

How can change Border Size of Selected Row in datagrid?

Thanks!