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":
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;
}