4

I have a DataGridView and currently it looks like the image below:

enter image description here

What I am trying to acheive is when I hover over any column of any row, the entire row should be highlighted and the background color should turn into a different color, just like in this image below.

enter image description here

Could you please help me with this problem?

Slai
  • 22,144
  • 5
  • 45
  • 53
black_belt
  • 6,601
  • 36
  • 121
  • 185

3 Answers3

11

You can highlight the row which is under mouse pointer in RowPostPaint event or you can override OnRowPostPaint method in a derived DataGridView. In this method you can paint the whole row yourself or just paint some part of it or draw something over the row:

enter image description here

Example

using System;
using System.Drawing;
using System.Windows.Forms;
public class MyDataGridView : DataGridView
{
    public MyDataGridView() { DoubleBuffered = true; }
    protected override void OnRowPostPaint(DataGridViewRowPostPaintEventArgs e)
    {
        base.OnRowPostPaint(e);
        if (this.RectangleToScreen(e.RowBounds).Contains(MousePosition))
        {
            using (var b = new SolidBrush(Color.FromArgb(50, Color.Blue)))
            using (var p = new Pen(Color.Blue))
            {
                var r = e.RowBounds; r.Width -= 1; r.Height -= 1;
                e.Graphics.FillRectangle(b, r);
                e.Graphics.DrawRectangle(p, r);
            }
        }
    }
    protected override void OnMouseMove(MouseEventArgs e){
        base.OnMouseMove(e); this.Invalidate();
    }
    protected override void OnMouseEnter(EventArgs e){
        base.OnMouseEnter(e); this.Invalidate();
    }
    protected override void OnMouseLeave(EventArgs e){
        base.OnMouseLeave(e); this.Invalidate();
    }
    protected override void OnScroll(ScrollEventArgs e){
        base.OnScroll(e); this.Invalidate();
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
4

What you see in your second screenshot is not a DataGridView but a ListView in Details mode.

listView1.View = View.Details;
listView1.FulRowSelect = true;

Use Columns to populate columns and Items for rows. The second and further columns in a row can be populated by SubItems property of each items.

By default the row will not be hovered automatically and the selection appearance is an ugly dark blue static line. To show this nice light blue hovering enable the "explorer" theme on your list view by a little trick:

internal sealed class AdvancedListView : ListView
{
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);

        if (!DesignMode && Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 6)
        {
            SetWindowTheme(Handle, "explorer", null);
        }
    }

    [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
    private extern static int SetWindowTheme(IntPtr hWnd, string pszSubAppName, string pszSubIdList);
}

Now you will get exactly the same look and feel as in your second screenshot...

well, except for the ordering. To enable that little arrow, which is also a hidden feature use the class below (note that it sorts by string values in this implementation):

internal class ListViewSorter : IComparer
{
    private const int HDI_FORMAT = 0x0004;
    private const int HDF_SORTUP = 0x0400;
    private const int HDF_SORTDOWN = 0x0200;
    private const int LVM_GETHEADER = 0x1000 + 31; // LVM_FIRST + 31
    private const int HDM_GETITEM = 0x1200 + 11; // HDM_FIRST + 11
    private const int HDM_SETITEM = 0x1200 + 12; // HDM_FIRST + 12

    private readonly int column;
    private readonly SortOrder sortOrder;


    public ListViewSorter(SortOrder sortOrder, int col, ListView listView)
    {
        IntPtr hColHeader = SendMessage(listView.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
        HDITEM hdItem = new HDITEM();
        IntPtr colHeader = new IntPtr(col);

        hdItem.mask = HDI_FORMAT;
        SendMessageItem(hColHeader, HDM_GETITEM, colHeader, ref hdItem);

        if (sortOrder == SortOrder.Ascending)
        {
            hdItem.fmt &= ~HDF_SORTDOWN;
            hdItem.fmt |= HDF_SORTUP;
        }
        else if (sortOrder == SortOrder.Descending)
        {
            hdItem.fmt &= ~HDF_SORTUP;
            hdItem.fmt |= HDF_SORTDOWN;
        }
        else if (sortOrder == SortOrder.None)
        {
            hdItem.fmt &= ~HDF_SORTDOWN & ~HDF_SORTUP;
        }

        SendMessageItem(hColHeader, HDM_SETITEM, colHeader, ref hdItem);
        this.sortOrder = sortOrder;
        this.column = col;
    }

    protected virtual int DoCompare(ListViewItem item1, ListViewItem item2)
    {
        return sortOrder == SortOrder.Ascending ? String.Compare(item1.SubItems[column].Text, item2.SubItems[column].Text, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase)
            : String.Compare(item2.SubItems[column].Text, item1.SubItems[column].Text, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase);
    }

    public int Compare(object x, object y)
    {
        ListViewItem item1 = (ListViewItem)x;
        ListViewItem item2 = (ListViewItem)y;
        return DoCompare(item1, item2);
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct HDITEM
    {
        public int mask;
        public int cxy;
        [MarshalAs(UnmanagedType.LPTStr)] public string pszText;
        public IntPtr hbm;
        public int cchTextMax;
        public int fmt;
        public int lParam;
        public int iImage;
        public int iOrder;
    };

    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", EntryPoint = "SendMessage")]
    private static extern IntPtr SendMessageItem(IntPtr handle, int msg, IntPtr wParam, ref HDITEM lParam);
}

To apply the sorting on the clicked column use the ColumnClick event:

private void advancedListView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
    advancedListView1.Sorting = advancedListView1.Sorting == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
    advancedListView1.ListViewItemSorter = new ListViewSorter(advancedListView1.Sorting, e.Column, advancedListView1);
}

In Windows 7 it will look like as in your screenshot. In my Windows 10 it looks like this:

Advanced ListView in Windows 10

György Kőszeg
  • 17,093
  • 6
  • 37
  • 65
  • Thank you very much for you answer. I have switched to ListView and I think I am very close to what I am trying to achieve. Could you please give me an example how I can use the `internal sealed class` with my ListView? – black_belt Feb 27 '17 at 16:14
  • You can make it public of course. And feel free to customize it if you need to sort by datetime/decimal/etc values. – György Kőszeg Feb 28 '17 at 10:06
  • Actually I am not sure how to use the class. I tried including the class just outside my form class, but the row doesn't get highlighted as expected. An example would really help me. Thank you very much. – black_belt Feb 28 '17 at 10:20
  • 1
    Oh I see. Just make it public and do a compile. Open the form in design mode. Now in the Toolbar you should see a * Components* group, which contains the `AdvancedListView` control. Drop that onto the form instead of the regular `ListView` – György Kőszeg Feb 28 '17 at 10:26
  • Thank you very much. It worked. I am a PHP developer, trying to learn C#. Learned an interesting thing from you today. :) – black_belt Feb 28 '17 at 11:35
1

you can do something like this :

private void dataGridView1_CellMouseMove(object sender, DataGridViewCellMouseEventArgs e)
{ DataGridView1.ClearSelection();
    If (e.RowIndex > -1) {  DataGridView1.Rows(e.RowIndex).Selected = True;  }          }

I think working with the background color of the row isnt possible, but i dont know for sure...

BrnPer
  • 21
  • 5