Home > OS >  Highlight search result in DataGridView cell
Highlight search result in DataGridView cell

Time:09-15

I'm creating a custom control, derived from DataGridView. I want to have a search functionality which highlights the result of the search on the cell text. Here's what I have so far:

protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
{
    if ((e.RowIndex <= -1) || (e.ColumnIndex <= -1) || string.IsNullOrWhiteSpace(HighlightedText) ||
        (e.Value is null) || (e.Value == DBNull.Value) || e.Value.GetType().IsImage())
    {
        base.OnCellPainting(e);
        return;
    }

    var cellText = e.FormattedValue.ToString();
    var ind = cellText.IndexOf(HighlightedText, StringComparison.OrdinalIgnoreCase);
    if (ind < 0)
    {
        base.OnCellPainting(e);
        return;
    }

    var newLineInd = cellText.IndexOf(Environment.NewLine);

    var rect = new Rectangle { Y = e.CellBounds.Y   2 };

    var before = cellText[..ind];

    var flags = TextFormatFlags.TextBoxControl;

    //I tried this but doesn't seem to work
    var pos = e.CellStyle.Alignment switch
    {
        DataGridViewContentAlignment.TopLeft => TextFormatFlags.Top | TextFormatFlags.Left,
        DataGridViewContentAlignment.TopCenter => TextFormatFlags.Top | TextFormatFlags.HorizontalCenter,
        DataGridViewContentAlignment.TopRight => TextFormatFlags.Top | TextFormatFlags.Right,
        DataGridViewContentAlignment.MiddleLeft => TextFormatFlags.VerticalCenter | TextFormatFlags.Left,
        DataGridViewContentAlignment.MiddleCenter => TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter,
        DataGridViewContentAlignment.MiddleRight => TextFormatFlags.VerticalCenter | TextFormatFlags.Right,
        DataGridViewContentAlignment.BottomLeft => TextFormatFlags.Bottom | TextFormatFlags.Left,
        DataGridViewContentAlignment.BottomCenter => TextFormatFlags.Bottom | TextFormatFlags.HorizontalCenter,
        DataGridViewContentAlignment.BottomRight => TextFormatFlags.Bottom | TextFormatFlags.Right,
        _ => TextFormatFlags.Left,
    };

    flags |= pos;
    var match = cellText.Substring(ind, HighlightedText.Length);
    var sizeBefore = TextRenderer.MeasureText(e.Graphics, before, e.CellStyle.Font, e.CellBounds.Size, flags);
    var sizeMatch = TextRenderer.MeasureText(e.Graphics, match, e.CellStyle.Font, e.CellBounds.Size, flags);

    if (sizeBefore.Width > 5)
    {
        rect.X = e.CellBounds.X   sizeBefore.Width - 5;
        rect.Width = sizeMatch.Width - 6;

        //if match is on the new line
        if ((newLineInd != -1) && (ind > newLineInd))
        {
            var cellRow2Index = ind - newLineInd;
            var breakWord = cellText.Substring(newLineInd, cellRow2Index);
            var s3 = TextRenderer.MeasureText(e.Graphics, breakWord, e.CellStyle.Font, e.CellBounds.Size,
                                              TextFormatFlags.TextBoxControl);
            rect.X = e.CellBounds.X   2   s3.Width - 7;
            rect.Y = e.CellBounds.Y   sizeMatch.Height   2;
        }
    }
    else
    {
        rect.X = e.CellBounds.X   2;
        rect.Width = sizeMatch.Width - 6;
    }

    if (newLineInd == -1)
    {
        var sizeCell = TextRenderer.MeasureText(e.Graphics, cellText, e.CellStyle.Font, e.CellBounds.Size,
                                                TextFormatFlags.TextBoxControl);
        if (sizeCell.Width < e.CellBounds.Width)
        {
            rect.Y = e.CellBounds.Y   ((e.CellBounds.Height - sizeCell.Height) / 2);
        }
    }

    rect.Height = sizeMatch.Height;
    e.Handled = true;
    e.PaintBackground(e.CellBounds, true);
    e.Graphics.FillRectangle(_highlightBrush, rect);
    e.PaintContent(e.CellBounds);
}

As you can see, the calculations are not precise and there are a lot of random numbers added which won't work for different DPIs. On top of that, I couldn't work out how to account for different text positions. I tried using the e.CellStyle.Alignment to modify the flags variable but it didn't work. In the picture below I'm highlighting "19": Highlighting 19

CodePudding user response:

Thanks @dr.null. Based on the links you've provided I wrote this:

protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
{
    if ((e.RowIndex <= -1) || (e.ColumnIndex <= -1) || string.IsNullOrWhiteSpace(HighlightedText) ||
        (e.Value is null) || (e.Value == DBNull.Value) || e.Value.GetType().IsImage())
    {
        base.OnCellPainting(e);
        return;
    }

    var zeroWidth = "|";
    var cellText = e.FormattedValue.ToString().Replace(" ", zeroWidth);
    var highlight = HighlightedText.Replace(" ", zeroWidth);
    var ind = cellText.IndexOf(HighlightedText, StringComparison.OrdinalIgnoreCase);
    if (ind < 0)
    {
        base.OnCellPainting(e);
        return;
    }

    int linesBefore = 0;
    var totalLines = 1   Regex.Matches(cellText, Environment.NewLine).Count;
    var newLineInd = cellText.IndexOf(Environment.NewLine);
    while (newLineInd >= 0 && newLineInd < ind)
    {
        linesBefore  ;
        cellText = cellText[(newLineInd   Environment.NewLine.Length)..];
        ind = cellText.IndexOf(HighlightedText, StringComparison.OrdinalIgnoreCase);
        newLineInd = cellText.IndexOf(Environment.NewLine);
    }

    var g = e.Graphics;
    using var stringFormat = ConvertToStringFormat(e.CellStyle.Alignment);
    if(RightToLeft == RightToLeft.Yes)
    {
        stringFormat.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
    }

    var zeroSize = g.MeasureString(zeroWidth, e.CellStyle.Font, e.CellBounds.Width, stringFormat).Width;
    var contentSize = g.MeasureString(cellText, e.CellStyle.Font, e.CellBounds.Width, stringFormat);
    var x = g.MeasureString(cellText[..ind], e.CellStyle.Font, e.CellBounds.Width, stringFormat).Width;
    var highlightWidth = g.MeasureString(cellText.Substring(ind, highlight.Length), e.CellStyle.Font,
        e.CellBounds.Width, stringFormat).Width;

    x = stringFormat.Alignment switch
    {
        StringAlignment.Center => x   ((e.CellBounds.Width - contentSize.Width) / 2) - (zeroSize / 2),
        StringAlignment.Far => x   (e.CellBounds.Width - contentSize.Width) - (zeroSize * 1.5f),
        StringAlignment.Near => x   (zeroSize / 2),
        _ => x
    };

    var totalContentHeight = totalLines * contentSize.Height;
    var top = stringFormat.LineAlignment switch
    {
        StringAlignment.Center => (e.CellBounds.Height - totalContentHeight) / 2,
        StringAlignment.Far => e.CellBounds.Bottom - totalContentHeight,
        StringAlignment.Near => 0,
        _ => 0
    };

    var y = top   (linesBefore * contentSize.Height);
    var highlightRect = new RectangleF(
        e.CellBounds.X   x,
        e.CellBounds.Y   y,
        highlightWidth,
        contentSize.Height);

    e.PaintBackground(e.CellBounds, true);
    g.FillRectangle(_highlightBrush, highlightRect);
    e.PaintContent(e.CellBounds);
    e.Handled = true;
}
  • Related