CellPaintingEvent of DataGridView - c#

I am trying to highlight List of strings in my dataGrid view. For this purpose, I used the existing code that highlights a keyword in dataGridView. but resultant code only higlights last occurrence of the list (i.e. last record fetched).
Here is What I've tried
private void dvg_ClauseSent_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
foreach (string sw in HighlightStrings) // HighlightStringsis the list of string containing all strings I need to highlight in DataGrid view
{
int strt = 0;
int cnt = -1;
int idx = -1;
TextFormatFlags flags = TextFormatFlags.Default | TextFormatFlags.NoPrefix;
if ((((e.RowIndex >= 0) && (e.ColumnIndex >= 0))))
{
e.Handled = true;
e.PaintBackground(e.CellBounds, true);
if (!string.IsNullOrEmpty(sw))
{
string val = e.FormattedValue.ToString();
int sindx = val.IndexOf(sw);
if ((sindx >= 0))
{
while (strt != -1)
{
strt = val.IndexOf(sw, idx + 1);
cnt += 1;
idx = strt;
if (strt != -1)
{
if (strt != 0 && ((strt + sw.Length) != val.Length))
{
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
// and the size of the search word
// paint the background behind the search word
e.Graphics.FillRectangle(hl_brush, hl_rect);
hl_brush.Dispose();
}
}
}
}
}
}
// paint the content as usual
e.PaintContent(e.CellBounds);
}
}
Attached ScreenShots
Following screenshot shows the strings that should appear as highligted in dataGridView
http://i42.tinypic.com/2dtrea1.png
Part of strings enclosed in ANGLE BRACKET followed/preceeded by |* / *| should be appeared as highlighted but only last entry is being highlighted.
http://i39.tinypic.com/30cbw9l.png
Any help will be appreciated...

Your code has this strange thing:
Rectangle hl_rect = new Rectangle();
hl_rect.Y = (e.CellBounds.Y + 2);
hl_rect.Height = (e.CellBounds.Height - 5);
You don't even initialize the X and the Width (hence they will be empty by default). So how could it be rendered???
I would like to talk this to everyone who will be asking questions, please post your actual code. Don't try simplifying it if you don't understand how wrong it is after being simplified. Except the code above, I don't find any thing which may cause the issue (it's of course not tested, just a quick scan). I've tried writing another code for you, also tested. The problem is we have to draw the string and measure the string together so that the Text Bound can be determined exactly. The TextRenderingHint.AntiAlias should also be used, although sometimes it works without that. The drawn text may look a little blurry however it can be partially eliminated by using a large font, the larger the better. Here is the code for you.
private void dataGridView1_CellPainting(object sender,
DataGridViewCellPaintingEventArgs e) {
if (e.RowIndex > -1 && e.ColumnIndex > -1 && e.Value != null) {
string value = e.Value.ToString();
foreach (var s in HighlightStrings) {
int i = 0;
while (i < value.Length && (i = value.IndexOf(s,i))!=-1) {
if (!e.Handled){
e.Handled = true;
e.PaintBackground(e.ClipBounds, true);
}
StringFormat sf = StringFormat.GenericTypographic;
sf.LineAlignment = StringAlignment.Center;
RectangleF textBounds = GetTextBounds(e.Graphics,
value, i, s.Length,
e.CellBounds,
e.CellStyle.Font, sf);
//highlight it
e.Graphics.FillRectangle(Brushes.Yellow, textBounds);
i += s.Length;
using (Brush brush = new SolidBrush(e.CellStyle.ForeColor)) {
//draw string , don't use PaintContent
e.Graphics.DrawString(value, e.CellStyle.Font, brush,
e.CellBounds, sf);
}
}
}
}
}
public RectangleF GetTextBounds(Graphics g, string text,
int subIndex, int subLength,
RectangleF layout,
Font font, StringFormat sf) {
var charRange = new CharacterRange(0, text.Length);
var subCharRange = new CharacterRange(subIndex, subLength);
sf.SetMeasurableCharacterRanges(new[]{ charRange, subCharRange });
var regions = g.MeasureCharacterRanges(text, font, layout, sf);
return regions.Length < 2 ? RectangleF.Empty : regions[1].GetBounds(g);
}
NOTE: The reason we have to use DrawString is as I said to measure the TextBound exactly. If you have some way to measure it exactly without having to draw the string yourself, you can use PaintContent.

Related

How to calculate cell height [that has long text] when printing DataGridView in C#

I searched but didn't find how to set the cell height when printing DataGridView when the cell has long text.
I didn't find one result that concentrate how to calculate the height with long text. And I don't want to use 3rd party DLL file that print it with right cell height.
I use this code to calculate the height but text always cut and short text has lower cell height that on DGV.
var tallestHeight = 0;
foreach (DataGridViewCell cell in GridRow.Cells)
{
if (!cell.Visible) { continue; }
var s = e.Graphics.MeasureString(cell.FormattedValue.ToString(), dataGridView1.Font);
var tempHeight = (int)(s.Height * Math.Ceiling(s.Width / dataGridView1.Columns[cell.ColumnIndex].Width));
if (tempHeight > tallestHeight)
{
tallestHeight = tempHeight;
}
tallestHeight = (tallestHeight < 22) ? 22 : tallestHeight;
}
iCellHeight = tallestHeight;
I want when I print DataGridView to printer to show all the text in all cells without cutting. Long text increase the row height and if no long text row's height stays unchanged.
I have row height = 22
Text wrap for my DataGridView is enabled
Edit1:
Here is my DataGridView properties
Here is how i print my DataGridView: PrintDGV Class that i use
My DGV appears
Print preview
Yellow highlighted text isn't complete Full text is First Middle lastname- some text- 0130011511478- تجربةة 7427/01300/8346584584563846
The text below it complete.
How to show the first row at full?
Apart from the grid settings, you need to use the output sizes to calculate the adequate height of each row to fit their contents. Also, calling a MeasureString method overload that takes StringFormat is necessary to get more accurate result.
I see in the printout image above you are dividing the MarginBounds.Width by the visible cells. Thus, the following:
Calculate
Create a method to calculate and return the proper height of each row.
// +
using System.Linq;
private int GetRowHeight(
Graphics g,
DataGridViewRow row,
Rectangle bounds,
StringFormat sf,
int minHeight = 22)
{
var cells = row.Cells.OfType<DataGridViewTextBoxCell>()
.Where(c => c.Visible);
if (cells == null) return minHeight;
var cell = cells.Aggregate((DataGridViewTextBoxCell)null, (x, y) => x != null &&
x.FormattedValue.ToString().Length > y.FormattedValue.ToString().Length ? x : y);
if (cell == null) return minHeight;
var h = g.MeasureString(cell.FormattedValue.ToString(),
row.DataGridView.Font,
new SizeF(bounds.Width / cells.Count(), bounds.Height),
sf).ToSize().Height;
return Math.Max(h + 6, minHeight); // 6 for top and bottom margins...
}
Call
Caller example and class variables to track the printing...
// +
using System.Drawing.Printing;
private int rowIndex;
private int cellCount;
private int pageNumber;
private readonly PrintDocument printDoc;
// ...
// ctor
public YourForm()
{
InitializeComponent();
printDoc = new PrintDocument();
printDoc.PrintPage += OnPrintPage;
}
// Cleanup
private void YourForm_FormClosed(object sender, FormClosedEventArgs e)
{
printDoc.Dispose();
}
// Preview
private void btnPrintPreview(object sender, EventArgs e) => Print(true);
// Print
private void btnPrint(object sender, EventArgs e) => Print();
// Print Routine
private void Print(bool preview = false)
{
rowIndex = 0;
cellCount = 0;
pageNumber = 0;
var rows = dataGridView1.Rows
.Cast<DataGridViewRow>()
.FirstOrDefault(r => !r.IsNewRow);
if (rows != null)
cellCount = rows.Cells
.OfType<DataGridViewTextBoxCell>()
.Where(c => c.Visible)
.Count();
if (cellCount == 0)
{
MessageBox.Show("Nothing to print...");
return;
}
printDoc.DefaultPageSettings.Landscape = true;
if (preview)
{
using (var pd = new PrintPreviewDialog())
{
pd.Document = printDoc;
pd.ShowDialog();
}
}
else
{
using (var pd = new PrintDialog())
{
pd.Document = printDoc;
if (pd.ShowDialog() == DialogResult.OK)
pd.Document.Print();
}
}
}
Print
PrintDocument.PrintPage event example.
// +
using System.Drawing.Text;
private void OnPrintPage(object sender, PrintPageEventArgs e)
{
var w = e.MarginBounds.Width / cellCount;
var x = e.MarginBounds.X;
var y = e.MarginBounds.Y;
int h;
Rectangle rec;
using (var sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
// Maybe you need to set this? I see Arabic text in the images.
// sf.FormatFlags = StringFormatFlags.DirectionRightToLeft;
e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
// Uncomment to print the headers in the first page only.
//if (pageNumber == 0)
//{
h = dataGridView1.RowTemplate.Height;
foreach (var col in dataGridView1.Columns
.OfType<DataGridViewTextBoxColumn>()
.Where(c => c.Visible))
{
rec = new Rectangle(x, y, w, h);
e.Graphics.FillRectangle(Brushes.Gainsboro, rec);
e.Graphics.DrawString(
col.HeaderText,
col.DataGridView.Font,
Brushes.Black,
rec,
sf);
e.Graphics.DrawRectangle(Pens.Black, rec);
x += w;
}
x = e.MarginBounds.X;
y += h;
//}
for (var i = rowIndex; i < dataGridView1.RowCount; i++)
{
var row = dataGridView1.Rows[i];
if (row.IsNewRow) break;
h = GetRowHeight(e.Graphics, row, e.MarginBounds, sf);
if (h > e.MarginBounds.Height)
{
MessageBox.Show("Insufficient height.");
e.Cancel = true;
return;
}
foreach (var cell in row.Cells
.OfType<DataGridViewTextBoxCell>()
.Where(c => c.Visible))
{
rec = new Rectangle(x, y, w, h);
if (rec.Bottom > e.MarginBounds.Bottom)
{
pageNumber++;
rowIndex = i;
e.HasMorePages = true;
return;
}
e.Graphics.DrawString(
cell.FormattedValue.ToString(),
dataGridView1.Font,
Brushes.Black,
rec,
sf);
e.Graphics.DrawRectangle(Pens.Black, rec);
x += rec.Width;
}
x = e.MarginBounds.X;
y += h;
}
}
}
Output

Show tooltip when dragging node in TreeViewAdv

When dragging a node, although I get the Move icon there is no tooltip text shown in my case as there is in the given SimpleExample, using Andrey Gliznetsov's TreeViewAdv.
This doesn't seem to be affected by adding a Tooltip object and using SetToolTip method. How do I get the tooltip to show the text (and icon) of the dragged node when dragging?
]
In TreeViewAdv.Input.cs, you will find this method
private void CreateDragBitmap(IDataObject data)
{
if (UseColumns || !DisplayDraggingNodes)
return;
which prevents a _dragBitmap being created if columns are being used. If you have to use the TreeViewAdv control dll without modification, then the only workaround if you want to see the tooltip is to not use columns (Property UseColumns = False).
But since the source code is provided, you could modify CreateDragBitmap by removing the condition UseColumns from the first if statement in this method and adding a condition inside the foreach NodeControl loop to exit after the first is drawn:
private void CreateDragBitmap(IDataObject data)
{
if (!DisplayDraggingNodes) //removed UseColumns as OR condition here
return;
TreeNodeAdv[] nodes = data.GetData(typeof(TreeNodeAdv[])) as TreeNodeAdv[];
if (nodes != null && nodes.Length > 0)
{
Rectangle rect = DisplayRectangle;
Bitmap bitmap = new Bitmap(rect.Width, rect.Height);
using (Graphics gr = Graphics.FromImage(bitmap))
{
gr.Clear(BackColor);
DrawContext context = new DrawContext();
context.Graphics = gr;
context.Font = Font;
context.Enabled = true;
int y = 0;
int maxWidth = 0;
foreach (TreeNodeAdv node in nodes)
{
if (node.Tree == this)
{
int x = 0;
int height = _rowLayout.GetRowBounds(node.Row).Height;
foreach (NodeControl c in NodeControls)
{
Size s = c.GetActualSize(node, context);
if (!s.IsEmpty)
{
int width = s.Width;
rect = new Rectangle(x, y, width, height);
x += (width + 1);
context.Bounds = rect;
c.Draw(node, context);
if (UseColumns) //only show first column if using columns
break;
}
}
y += height;
maxWidth = Math.Max(maxWidth, x);
}
}
if (maxWidth > 0 && y > 0)
{
_dragBitmap = new Bitmap(maxWidth, y, PixelFormat.Format32bppArgb);
using (Graphics tgr = Graphics.FromImage(_dragBitmap))
tgr.DrawImage(bitmap, Point.Empty);
BitmapHelper.SetAlphaChanelValue(_dragBitmap, 150);
}
else
_dragBitmap = null;
}
}
}
which results in
If you wanted to see all columns when dragging, omit the if (UseColumns) break; change, which looks like this

How to highlight wrapped text in a control using the graphics?

I need to highlight the particular character in a control using the fill rect.
I can get the location of the text when it's not wrapped by using the Graphics.MeasureString() method like below,
var size = g.MeasureString(tempSearchText, style.Font, 0, StringFormat.GenericTypographic);
If the text is wrapped then I'm not able to find the exact bounds of the character to highlight the text.
I need to get the exact bounds of the given character in the text which is wrapped. Provide your suggestion to achieve this scenario.
There is no clear specification of which controls to target, so I'm testing 3 different:
TextBox, RichTextbox and ListBox.
TextBox and RichTextbox have the same behavior and share the same tools, so there's no need to define two different methods to achieve the same result.
Of course RichTextbox offers many more options, including RTF.
Also, I'm testing both Graphics.DrawString() and TextRenderer.DrawText().
This is the result of this test, so it's more clear what the code does.
Warning:
For this example I'm using Control.CreateGraphics(), because TextBox and RichTextBox controls don't provide a Paint() event. For a real world application, you should create a Custom Control derived from TextBox or RichTextBox, override WndPrc and handle WM_PAINT.
1) Highlight all t in a multiline TextBox control.
TextRenderer->DrawText():
//Define some useful flags for TextRenderer
TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.Top |
TextFormatFlags.NoPadding | TextFormatFlags.WordBreak |
TextFormatFlags.TextBoxControl;
//The char to look for
char TheChar = 't';
//Find all 't' chars indexes in the text
List<int> TheIndexList = textBox1.Text.Select((chr, idx) => chr == TheChar ? idx : -1)
.Where(idx => idx != -1).ToList();
//Or with Regex - same thing, pick the one you prefer
List<int> TheIndexList = Regex.Matches(textBox1.Text, TheChar.ToString())
.Cast<Match>()
.Select(chr => chr.Index).ToList();
//Using .GetPositionFromCharIndex(), define the Point [p] where the highlighted text is drawn
if (TheIndexList.Count > 0)
{
foreach (int Position in TheIndexList)
{
Point p = textBox1.GetPositionFromCharIndex(Position);
using (Graphics g = textBox1.CreateGraphics())
TextRenderer.DrawText(g, TheChar.ToString(), textBox1.Font, p,
textBox1.ForeColor, Color.LightGreen, flags);
}
}
The same operation using Graphics.FillRectangle() and Graphics.DrawString():
if (TheIndexList.Count > 0)
{
using (Graphics g = textBox1.CreateGraphics())
{
foreach (int Position in TheIndexList)
{
PointF pF = textBox1.GetPositionFromCharIndex(Position);
SizeF sF = g.MeasureString(TheChar.ToString(), textBox1.Font, 0,
StringFormat.GenericTypographic);
g.FillRectangle(Brushes.LightGreen, new RectangleF(pF, sF));
using (SolidBrush brush = new SolidBrush(textBox1.ForeColor))
{
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
g.DrawString(TheChar.ToString(), textBox1.Font, brush, pF, StringFormat.GenericTypographic);
}
}
}
}
There is no notable difference in behavior: TextRenderer.DrawText()
and Graphics.DrawString() do the exact same thing here.
Setting Application.SetCompatibleTextRenderingDefault() to true or
false does not seem to have any affect (in the current context, at least).
2) Highlight some string patterns ("Words") in a TextBox control and a multiline RichTextbox control.
Using TextRenderer only, since there's no difference in behavior.
I'm simply letting IndexOf() find the the first occurrence of the
strings, but the same search pattern used before can take it's place. Regex works better.
string[] TheStrings = {"for", "s"};
foreach (string pattern in TheStrings)
{
Point p = TextBox2.GetPositionFromCharIndex(TextBox2.Text.IndexOf(pattern));
using (var g = TextBox2.CreateGraphics()) {
TextRenderer.DrawText(g, pattern, TextBox2.Font, p,
TextBox2.ForeColor, Color.LightSkyBlue, flags);
}
}
TheStrings = new string []{"m", "more"};
foreach (string pattern in TheStrings)
{
Point p = richTextBox1.GetPositionFromCharIndex(richTextBox1.Text.IndexOf(pattern));
using (Graphics g = richTextBox1.CreateGraphics())
TextRenderer.DrawText(g, pattern, richTextBox1.Font, p,
richTextBox1.ForeColor, Color.LightSteelBlue, flags);
}
3) Highlight all s in all the ListItems of a ListBox control (of course it can be any other string :)
The ListBox.DrawMode is set to Normal and changed "on the fly" to OwnerDrawVariable to evaluate whether TextRenderer and Graphics behave differently here.
There is a small difference: a different offset, relative to the left
margin of the ListBox, compared to the standard implementation.
TextRenderer, with TextFormatFlags.NoPadding renders 2 pixels to the
left (the opposite without the flag). Graphics renders 1 pixel to the
right. Of course if OwnerDrawVariable is set in design mode,
this will not be noticed.
string HighLightString = "s";
int GraphicsPaddingOffset = 1;
int TextRendererPaddingOffset = 2;
private void button1_Click(object sender, EventArgs e)
{
listBox1.DrawMode = DrawMode.OwnerDrawVariable;
}
How the following code works:
Get all the positions in the ListItem text where the pattern (string HighLightString) appears.
Define an array of CharacterRange structures with the position and length of the pattern.
Fill a StringFormat with all the CharacterRange structs using .SetMeasurableCharacterRanges()
Define an array of Regions using Graphics.MeasureCharacterRanges() passing the initialized StringFormat.
Define an array of Rectangles sized using Region.GetBounds()
Fill all the Rectangles with the highlight color using Graphics.FillRectangles()
Draw the ListItem text.
TextRenderer.DrawText() implementation:
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.Top | TextFormatFlags.NoPadding |
TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl;
Rectangle bounds = new Rectangle(e.Bounds.X + TextRendererPaddingOffset,
e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
string ItemString = listBox1.GetItemText(listBox1.Items[e.Index]);
List<int> TheIndexList = Regex.Matches(ItemString, HighLightString)
.Cast<Match>()
.Select(s => s.Index).ToList();
if (TheIndexList.Count > 0)
{
CharacterRange[] CharRanges = new CharacterRange[TheIndexList.Count];
for (int CharX = 0; CharX < TheIndexList.Count; CharX++)
CharRanges[CharX] = new CharacterRange(TheIndexList[CharX], HighLightString.Length);
StringFormat format = new StringFormat(StringFormat.GenericDefault);
format.SetMeasurableCharacterRanges(CharRanges);
Region[] regions = e.Graphics.MeasureCharacterRanges(ItemString, e.Font, e.Bounds, format);
RectangleF[] rectsF = new RectangleF[regions.Length];
for (int RFx = 0; RFx < regions.Length; RFx++)
rectsF[RFx] = regions[RFx].GetBounds(e.Graphics);
e.Graphics.FillRectangles(Brushes.LightGreen, rectsF);
}
TextRenderer.DrawText(e.Graphics, ItemString, e.Font, bounds, e.ForeColor, flags);
}
`Graphics.DrawString()` implementation
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
Rectangle bounds = new Rectangle(e.Bounds.X - GraphicsPaddingOffset,
e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
string ItemString = listBox1.GetItemText(listBox1.Items[e.Index]);
List<int> TheIndexList = Regex.Matches(ItemString, HighLightString)
.Cast<Match>()
.Select(s => s.Index).ToList();
StringFormat format = new StringFormat(StringFormat.GenericDefault);
if (TheIndexList.Count > 0)
{
CharacterRange[] CharRanges = new CharacterRange[TheIndexList.Count];
for (int CharX = 0; CharX < TheIndexList.Count; CharX++)
CharRanges[CharX] = new CharacterRange(TheIndexList[CharX], HighLightString.Length);
format.SetMeasurableCharacterRanges(CharRanges);
Region[] regions = e.Graphics.MeasureCharacterRanges(ItemString, e.Font, e.Bounds, format);
RectangleF[] rectsF = new RectangleF[regions.Length];
for (int RFx = 0; RFx < regions.Length; RFx++)
rectsF[RFx] = regions[RFx].GetBounds(e.Graphics);
e.Graphics.FillRectangles(Brushes.LightGreen, rectsF);
}
using (SolidBrush brush = new SolidBrush(e.ForeColor))
e.Graphics.DrawString(ItemString, e.Font, brush, bounds, format);
}
Note: Depending on the ListBox.DrawMode, it may become necessary to
subscribe the ListBox.MeasureItem() event or set the .ItemHeight
property to the corrent value.
private void listBox1_MeasureItem(object sender, MeasureItemEventArgs e)
{
e.ItemHeight = listBox1.Font.Height;
}

Resize font if text doesnt fit into cell (XRTable)

How to resize cell content (font size) if it doesn't fit. I don't want word-wrap or change cell's size since the form has to fit on single page. The text might have various lengths. It might contain spaces but doesn't have to.
#edit
I've made a mistake. The control I meant is not a XtraGrid but XRTable.
I suggest you to look at the Appearances at first. There are multiple ways to customize appearances of individual rows and cells.
If these options are not helpful, you can manually draw cell content as you needed using the Custom Drawing feature. For example, you can use the GridView.CustomDrawCell event to check whether or not the cell's content is exceed the cell's bounds and update the font of this cell accordingly.
Related example: How to: Custom Draw Cells Depending Upon Cell Values
You can change the font of the cell where the text flows as follows
private void gvView_CustomDrawCell(object sender, DevExpress.XtraGrid.Views.Base.RowCellCustomDrawEventArgs e)
{
if (e.Column != null && e.Column.Name == bgcStav.Name)
{
float minFontSize = 6;
string text = "teeeeeeeeeeeeeext";
int minWidth = gvView.CalcColumnBestWidth(bgcStav);
SizeF s = e.Appearance.CalcTextSize(e.Graphics, text, minWidth);
if (s.Width >= minWidth)
{
e.Appearance.Font = new Font(e.Appearance.Font.FontFamily, minFontSize);
}
}
}
but it is much better to trim the text if it overflows (you do not know how long the text can be), when you do not want to use wordwrap
private void gvView_CustomDrawCell(object sender, DevExpress.XtraGrid.Views.Base.RowCellCustomDrawEventArgs e)
{
if (e.Column != null && e.Column.Name == bgcStav.Name)
{
string text = e.DisplayText;
string newText = "";
int maxWidth = e.Bounds.Width - 20;
SizeF textSize =e.Graphics.MeasureString(text, e.Appearance.Font);
if (textSize.Width >= maxWidth)
{
string textPom = "";
for (int i = 0; i < text.Length; i++)
{
textPom = text.Substring(0, i) + "...";
textSize = e.Graphics.MeasureString(textPom, e.Appearance.Font);
if (textSize.Width >= maxWidth)
{
newText = text.Substring(0, i - 1) + "...";
break;
}
}
e.DisplayText = newText;
}
}
}
Advantage of this solution is that the crop only what is displaied, but in the datatable text remains in its original form

DataGridView CellPainting: Highlight word in cell considering cell's content direction

This is the case: I want to search for a word in a whole DataGridView through looping it's Rows, and then for each match I highlight the word. The major inflexibility I face is that columns have different System.Windows.Forms.DataGridViewContentAlignments. So in paiting (highlighting) mathced word in cells I should take care of cell's ContentAlignment.
So far I have written the following code to find the matches
private int FindAllMatches()
{
int itemsFound = 0;
for (int r = 0; r < dgvMain.Rows.Count; r++)
{
DataGridViewRow row = dgvMain.Rows[r];
for (int c = 0; c < Columns.Count; c++)
{
string cellValue = (dgvMain.Rows[r].Cells[c].Value ?? "").ToString();
if (cellValue.Contains(SearchValue.ToString()))
{
HighlightRow(row); // highlights whole row, weak solution
itemsFound++;
break;
}
}
}
return itemsFound;
}
But as you see I have currently managed to highlight the rows which have at least one match. What I need is a code to highlight only the portion of cell which matches my specific word (SearchValue).
I know I should use the CellPainting Event, but I don't know how to paint the word inside a Cell considering the Cell's ContentAlignment
I have modified the my answer to this question to consider the direction and content alignment. It works for default alignment but you have to improve the code for other alignments.
void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex != -1 && e.Value != null && e.Value.ToString().Length > 5 && e.ColumnIndex == 2)
{
if (!e.Handled)
{
e.Handled = true;
e.PaintBackground(e.CellBounds, dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Selected);
}
if ((e.PaintParts & DataGridViewPaintParts.ContentForeground) != DataGridViewPaintParts.None)
{
bool rightToLeft = this.RightToLeft == RightToLeft.Yes ? true:false;
string text = e.Value.ToString();
string textPart1 = text.Substring(0, text.Length - 5);
string textPart2 = text.Substring(text.Length - 5, 5);
Size fullsize = TextRenderer.MeasureText(text, e.CellStyle.Font);
Size size1 = TextRenderer.MeasureText(textPart1, e.CellStyle.Font);
Size size2 = TextRenderer.MeasureText(textPart2, e.CellStyle.Font);
Rectangle rect1 = new Rectangle(e.CellBounds.Location, e.CellBounds.Size);
TextFormatFlags flags = GetFlagsForCellStyleAlignment(rightToLeft, e.CellStyle.Alignment);
TextRenderer.DrawText(e.Graphics, text, e.CellStyle.Font, rect1, Color.Crimson, flags);
using (Brush cellForeBrush = new SolidBrush(e.CellStyle.ForeColor))
{
TextRenderer.DrawText(e.Graphics, textPart1, e.CellStyle.Font, rect1, e.CellStyle.ForeColor, flags);
}
}
}
}
TextFormatFlags GetFlagsForCellStyleAlignment(bool rigthToLeft, DataGridViewContentAlignment alignment)
{
TextFormatFlags flags = TextFormatFlags.Default;
switch (alignment)
{
case DataGridViewContentAlignment.TopLeft:
{
flags = TextFormatFlags.Default;
if (rigthToLeft)
{
flags |= TextFormatFlags.Right;
}
break;
}
case DataGridViewContentAlignment.MiddleLeft:
{
flags = TextFormatFlags.VerticalCenter;
if (rigthToLeft)
{
flags |= TextFormatFlags.Right;
}
break;
}
}
if (rigthToLeft)
flags |= TextFormatFlags.RightToLeft;
return flags;
}

Categories