How to highlight search Arabic text in DataGridView?

Viewed 320

I want to highlight the given search text in the DataGridView but the data is in Arabic. I have tried CellPainting event to find the bounds of the search text and draw FillRectangle, but I could not exactly get the bounds of the search text.

UI

The following code was the one I used:

private void dgv_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    // High light and searching apply over selective fields of grid.  
    if (e.RowIndex > -1 && e.ColumnIndex > -1 && dgv.Columns[e.ColumnIndex].Name != "Id")
    {
        // Check data for search  
        if (!String.IsNullOrWhiteSpace(txtSearch.Text.Trim()))
        {
            String gridCellValue = e.Value.ToString();
            // check the index of search text into grid cell.  
            int startIndexInCellValue = gridCellValue.IndexOf(txtSearch.Text.Trim());
            // IF search text is exists inside grid cell then startIndexInCellValue value will be greater then 0 or equal to 0  
            if (startIndexInCellValue >= 0)
            {
                e.Handled = true;
                e.PaintBackground(e.CellBounds, true);
                //the highlite rectangle  
                Rectangle hl_rect = new Rectangle();
                hl_rect.Y = e.CellBounds.Y + 2;
                hl_rect.Height = e.CellBounds.Height - 5;
                //find the size of the text before the search word in grid cell data.  
                String sBeforeSearchword = gridCellValue.Substring(0, startIndexInCellValue);
                //size of the search word in the grid cell data  
                String sSearchWord = gridCellValue.Substring(startIndexInCellValue, txtSearch.Text.Trim().Length);
                Size s1 = TextRenderer.MeasureText(e.Graphics, sBeforeSearchword, e.CellStyle.Font, e.CellBounds.Size);
                Size s2 = TextRenderer.MeasureText(e.Graphics, sSearchWord, e.CellStyle.Font, e.CellBounds.Size);
                if (s1.Width > 5)
                {
                    hl_rect.X = e.CellBounds.Right + s1.Width - e.CellBounds.X - e.CellBounds.Left;
                    hl_rect.Width = s2.Width - 6;
                }
                else
                {
                    hl_rect.X = e.CellBounds.X + 2;
                    hl_rect.Width = s2.Width - 6;
                }
                //color for showing highlighted text in grid cell  
                SolidBrush hl_brush;
                hl_brush = new SolidBrush(Color.Yellow);
                //paint the background behind the search word  
                e.Graphics.FillRectangle(hl_brush, hl_rect);
                hl_brush.Dispose();
                e.PaintContent(e.CellBounds);
            }
        }
    }
}
1 Answers

Problem


You need to take into account several factors for each cell:

  • The DataGridViewContentAlignment.
  • The exact size of the selected characters.
  • The zero-width characters (white spaces).
  • The size of the content.
  • The empty space.
  • The index of the first occurrence of the search string.

All the mentioned are necessary to calculate and adjust both the location and size of the highlight rectangle.

Here's an example:

RTL Languages - RTL Layout


private void dgv_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    if (txtSearch.TextLength < 1 
        || e.RowIndex < 0 
        || e.ColumnIndex < 1
        || e.Value == null)
        return;

    var zeroWidth = "|";
    var v = e.Value.ToString().Replace(" ", zeroWidth);
    var f = txtSearch.Text.Replace(" ", zeroWidth);
    var i = v.IndexOf(f, StringComparison.InvariantCultureIgnoreCase);

    if (i < 0) return;

    e.Handled = true;
    var g = e.Graphics;

    using (var sf = ToStringFormat(e.CellStyle.Alignment))
    {
        var zw = g.MeasureString(zeroWidth, e.CellStyle.Font, e.CellBounds.Width, sf).Width;
        var valWidth = g.MeasureString(v, e.CellStyle.Font, e.CellBounds.Width, sf).Width;
        var w = g.MeasureString(f, e.CellStyle.Font, e.CellBounds.Width, sf).Width;
        var x = e.CellBounds.Right - ((e.CellBounds.Width - valWidth) / 2);

        x -= g.MeasureString(v.Substring(0, i), e.CellStyle.Font,
            e.CellBounds.Width, sf).Width;
        x -= w;

        switch (e.CellStyle.Alignment)
        {
            case DataGridViewContentAlignment.BottomLeft:
            case DataGridViewContentAlignment.MiddleLeft:
            case DataGridViewContentAlignment.TopLeft:
                x += ((e.CellBounds.Width - valWidth) / 2) - zw;
                break;
            case DataGridViewContentAlignment.MiddleRight:
            case DataGridViewContentAlignment.BottomRight:
            case DataGridViewContentAlignment.TopRight:
                x -= ((e.CellBounds.Width - valWidth) / 2) - zw;
                break;
            default:                        
                break;
        }

        var r = new RectangleF(
            x, 
            e.CellBounds.Y + 3, 
            w, 
            e.CellBounds.Height - 7);

        e.PaintBackground(e.CellBounds, true);
        g.FillRectangle(Brushes.Yellow, r);
        e.PaintContent(e.CellBounds);
    }
}

private StringFormat ToStringFormat(DataGridViewContentAlignment ca)
{
    var sf = StringFormat.GenericTypographic;

    switch (ca)
    {
        case DataGridViewContentAlignment.MiddleCenter:
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;
            break;
        case DataGridViewContentAlignment.MiddleLeft:
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Center;
            break;
        case DataGridViewContentAlignment.MiddleRight:
            sf.Alignment = StringAlignment.Far;
            sf.LineAlignment = StringAlignment.Center;
            break;
        case DataGridViewContentAlignment.BottomCenter:
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Far;
            break;
        case DataGridViewContentAlignment.BottomLeft:
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Far;
            break;
        case DataGridViewContentAlignment.BottomRight:
            sf.Alignment = StringAlignment.Far;
            sf.LineAlignment = StringAlignment.Far;
            break;
        case DataGridViewContentAlignment.TopLeft:
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Near;
            break;
        case DataGridViewContentAlignment.TopRight:
            sf.Alignment = StringAlignment.Far;
            sf.LineAlignment = StringAlignment.Near;
            break;
        case DataGridViewContentAlignment.TopCenter:
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Near;
            break;
    }

    sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;

    return sf;
}

Here's a demo.

SOQ60983817A

Note: Only the DGV is set to RTL layout in the demo.

LTR Languages - LTR Layout


Maybe out of scope, however might be useful for someone.

private void dgv_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    if (txtSearch.TextLength < 1
        || e.RowIndex < 0
        || e.ColumnIndex < 1
        || e.Value == null)
        return;

    var zeroWidth = "|";
    var v = e.Value.ToString().Replace(" ", zeroWidth);
    var f = txtSearch.Text.Replace(" ", zeroWidth);
    var i = v.IndexOf(f, StringComparison.InvariantCultureIgnoreCase);

    if (i < 0) return;

    e.Handled = true;

    var g = e.Graphics;

    using (var sf = ToStringFormat(e.CellStyle.Alignment))
    {
        var zs = g.MeasureString(zeroWidth, e.CellStyle.Font, 
            e.CellBounds.Width, sf).Width;
        var valWidth = g.MeasureString(v, e.CellStyle.Font, 
            e.CellBounds.Width, sf).Width;
        var x = g.MeasureString(v.Substring(0, i), e.CellStyle.Font, 
            e.CellBounds.Width, sf).Width;
        var w = g.MeasureString(v.Substring(i, f.Length), e.CellStyle.Font, 
            e.CellBounds.Width, sf).Width;


        switch (e.CellStyle.Alignment)
        {
            case DataGridViewContentAlignment.MiddleCenter:
            case DataGridViewContentAlignment.BottomCenter:
            case DataGridViewContentAlignment.TopCenter:
                x += (e.CellBounds.Width - valWidth) / 2;
                x -= zs / 2;
                break;
            case DataGridViewContentAlignment.MiddleRight:
            case DataGridViewContentAlignment.BottomRight:
            case DataGridViewContentAlignment.TopRight:
                x += (e.CellBounds.Width - valWidth);
                x -= zs * 1.5f;
                break;
            default:
                x += zs / 2;
                break;
        }

        var r = new RectangleF(
            e.CellBounds.X + x,
            e.CellBounds.Y + 3,
            w,
            e.CellBounds.Height - 7);

        e.PaintBackground(e.CellBounds, true);
        g.FillRectangle(Brushes.Yellow, r);
        e.PaintContent(e.CellBounds);
    }
}

private StringFormat ToStringFormat(DataGridViewContentAlignment ca)
{
    var sf = StringFormat.GenericTypographic;

    switch (ca)
    {
        case DataGridViewContentAlignment.MiddleCenter:
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;
            break;
        case DataGridViewContentAlignment.MiddleLeft:
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Center;
            break;
        case DataGridViewContentAlignment.MiddleRight:
            sf.Alignment = StringAlignment.Far;
            sf.LineAlignment = StringAlignment.Center;
            break;
        case DataGridViewContentAlignment.BottomCenter:
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Far;
            break;
        case DataGridViewContentAlignment.BottomLeft:
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Far;
            break;
        case DataGridViewContentAlignment.BottomRight:
            sf.Alignment = StringAlignment.Far;
            sf.LineAlignment = StringAlignment.Far;
            break;
        case DataGridViewContentAlignment.TopLeft:
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Near;
            break;
        case DataGridViewContentAlignment.TopRight:
            sf.Alignment = StringAlignment.Far;
            sf.LineAlignment = StringAlignment.Near;
            break;
        case DataGridViewContentAlignment.TopCenter:
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Near;
            break;
    }

    return sf;
}

SOQ60983817B

Related