I have a DataGridView with several columns and several rows of data. One of the columns is a DataGridViewCheckBoxColumn and (based on the other data in the row) I would like the option to "hide" the checkbox in some rows. I know how to make it read only but I would prefer it to not show up at all or at least display differently (grayed out) than the other checkboxes. Is this possible?
Some workaround: make it read-only and change back color to gray.
For one specific cell:
dataGridView1.Rows[2].Cells[1].Style.BackColor = Color.LightGray;
dataGridView1.Rows[2].Cells[1].ReadOnly = true;
Or, better but more "complicated" solution:
suppose you have 2 columns: first with number, second with checkbox, that should not be visible when number > 2. You can handle CellPainting event, paint only borders (and eg. background) and break painting of rest. Add event CellPainting for DataGridView (optionally test for DBNull value to avoid exception when adding new data in empty row):
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
//check only for cells of second column, except header
if ((e.ColumnIndex == 1) && (e.RowIndex > -1))
{
//make sure not a null value
if (dataGridView1.Rows[e.RowIndex].Cells[0].Value != DBNull.Value)
{
//put condition when not to paint checkbox
if (Convert.ToInt32(dataGridView1.Rows[e.RowIndex].Cells[0].Value) > 2)
{
e.Paint(e.ClipBounds, DataGridViewPaintParts.Border | DataGridViewPaintParts.Background); //put what to draw
e.Handled = true; //skip rest of painting event
}
}
}
}
It should work, however if you change value manually in first column, where you check condition, you must refresh the second cell, so add another event like CellValueChanged:
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0)
{
dataGridView1.InvalidateCell(1, e.RowIndex);
}
}
http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridviewcheckboxcell.aspx
DataGridViewCheckBoxCell.Visible = false;
Edit: Oh, wait, it's read only. Derp.
In which case, try replacing the cell with an empty DataGridViewTextBoxCell.
Taken from Customize the Appearance of Cells in the Windows Forms DataGridView Control, you could catch the CellPainting event and not draw the cell if its in read only mode. For example:
public Form1()
{
InitializeComponent();
dataGridView1.CellPainting += new
DataGridViewCellPaintingEventHandler(dataGridView1_CellPainting);
}
private void dataGridView1_CellPainting(object sender,
System.Windows.Forms.DataGridViewCellPaintingEventArgs e)
{
// Change 2 to be your checkbox column #
if (this.dataGridView1.Columns[2].Index == e.ColumnIndex && e.RowIndex >= 0)
{
// If its read only, dont draw it
if (dataGridView1[e.ColumnIndex, e.RowIndex].ReadOnly)
{
// You can change e.CellStyle.BackColor to Color.Gray for example
using (Brush backColorBrush = new SolidBrush(e.CellStyle.BackColor))
{
// Erase the cell.
e.Graphics.FillRectangle(backColorBrush, e.CellBounds);
e.Handled = true;
}
}
}
}
The only caveat is that you need to call dataGridView1.Invalidate(); when you change the ReadOnly property of one of the DataGridViewCheckBox cell's.
Related
I have a snippet of code but it doesn't work very well, why?
1 - When starting the application, it is painting all records pink, only when I select all painted that it works normally again.
2 - When selecting two or more cells and then selecting only one of the selected cells, the painting of the line fails.
I would like to paint the entire line of the selected cell with the property selecionMode = CellSelect and multiSelect = true, so that when I select one or several cells, I paint the corresponding lines.
what i want to happen
private void DataGridView1_CellStateChanged(object sender, DataGridViewCellStateChangedEventArgs e)
{
var dataGrid = (DataGridView)sender;
var s = e.StateChanged;// = DataGridViewElementStates.Selected;
if (e.Cell.RowIndex > -1)
{
DataGridViewRow row = dataGrid.Rows[e.Cell.RowIndex];
if (e.Cell.Selected)
{
row.DefaultCellStyle.BackColor = Color.Pink;
}
else if (!e.Cell.Selected)
{
row.DefaultCellStyle.BackColor = Color.White;
}
}
}
I understand that all rows are selected when loading the data, which simply selects all rows from the included data. At this time, There is no "unselect" and so it paints all lines.
I solved the case in the following way:
Disabling the grid before loading data:
dataGridView1.Enabled = false;
dataGridView1.DataSource = data;
During the line painting process, I validate if the grid is enabled, to know that it is at the time of loading the data:
private void dataGridView1_CellStateChanged(object sender, DataGridViewCellStateChangedEventArgs e)
{
var s = e.StateChanged;// = DataGridViewElementStates.Selected;
if (e.Cell.RowIndex > -1)
{
DataGridViewRow row = ((DataGridView)sender).Rows[e.Cell.RowIndex];
if ((e.Cell.Selected) && ((DataGridView)sender).Enabled)
{
row.DefaultCellStyle.BackColor = Color.Pink;
}
else if (!e.Cell.Selected)
{
row.DefaultCellStyle.BackColor = Color.White;
}
}
}
At the end, I enable the grid again in the "dataGridView1_Paint" event:
private void dataGridView1_Paint(object sender, PaintEventArgs e)
{
dataGridView1.Enabled = true;
}
An important detail is that the "Paint" event is invoked all the time, making it impossible for the grid to be disabled later if necessary within the business model. If there is no such possibility, the flow will work perfectly. It was the only way I found.
Good day people of stackoverflow,
I am building a GUI and in this GUI I have a datagridview which shows data from a database. However I am looking for a way to change the color of an entire row to red when the first column of that row holds the string "Aborted".
I've tried finding a solution to this, however all examples have been conditions with Integers instead of Strings.
This is my current piece of code for the coloring:
private void datagridview1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 1 && e.Value as string == "Aborted")
{
var style = datagridview1.Rows[e.RowIndex].DefaultCellStyle;
style.BackColor = Color.Red;
style.ForeColor = Color.White;
}
}
However this does not change the color of the column or the entire row that holds the value Aborted nor does it even give an error message...
Even in the Events Properties of the datagridview1 next to CellFormatting it shows datagridview1_CellFormatting so it surely is bound to the datagridview.
Screenshot
Events Properties of the datagridview1
Screenshot of the datagridview
enter image description here
Does anyone have a solution to this? I have absolutely no idea what is going wrong.
EDIT: quick screenshot of an error
Error of #Jimi's example
You are manipulating the wrong style object. Try this:
private void datagridview1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 1 && e.Value as string == "Aborted")
{
e.CellStyle.BackColor = Color.Red;
e.CellStyle.ForeColor = Color.White;
}
}
The solution of Oliver works great if you wish to only color the column that has "Aborted", however the solution to color the entire row is as follows (Many thanks to u/JTarsier from Reddit)
// Use the event CellFormatting from Datagridview > Properties > Events
private void datagridview1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// Use this one liner to check which number is associated with which column
//(so you don't make the mistake of trying to use the wrong column like me ._.)
// Debug.WriteLine($"{e.ColumnIndex} : '{e.Value}'");
if (e.ColumnIndex == 0 && e.Value as string == "Aborted")
{
var style = datagridview1.Rows[e.RowIndex].DefaultCellStyle;
style.BackColor = Color.Red;
style.ForeColor = Color.White;
}
}
Thanks for all the help!
I want to hide one or two grid cells from my datagriview. But with this code all the grids are hidden and this is not what I want.
I want to hide one or two rectangles cells from my Datagridview.
I do not want to hide columns or data which Contain my cells .
I just wanna hide a Specified cells.
dataGridView1.CellBorderStyle = DataGridViewCellBorderStyle.None;
The recommended way to hide or modify cell border style is to code the CellPainting event.
Don't worry, no actual painting is required. All you need to do is set a few fields in the e.AdvancedBorderStyle parameter.
Here is an example:
Note the 'vertically merged' look of of the cells in the 3rd column; same for the 'horizontally merged' cells at the bottom. Also the double border of a cell in the 5th column.
private void dataGridView1_CellPainting(object sender,
DataGridViewCellPaintingEventArgs e)
{
if (e.ColumnIndex == 2 && e.RowIndex == 6)
e.AdvancedBorderStyle.Right = DataGridViewAdvancedCellBorderStyle.None;
if (e.ColumnIndex == 2 && e.RowIndex == 1)
e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None;
if (e.ColumnIndex == 4 && e.RowIndex == 4)
{
e.AdvancedBorderStyle.All = DataGridViewAdvancedCellBorderStyle.InsetDouble;
e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.Single;
}
}
Note that hiding borders is rather straight forward : Simply hide the right or the bottom border; other borderstyles require some trial and error (or a deeper understanding ;-)
Here I first set the style for all sides but as it paints the botton white (at least that's what I think it does) I then set the botton border back to single.
You may want to streamline the way the checks are done; this is just a simple example.
Update:
Here is a code to make the merging more dynamic: Use the mergeCells function to mark a cell for merging or un-merging with its right or bottom neighbour:
private void mergeCells(DataGridViewCell cell, bool mergeH, bool mergeV)
{
string m = "";
if (mergeH) m += "R"; // merge horizontally by hiding the right border line
if (mergeV) m += "B"; // merge vertically by hiding the bottom border line
cell.Tag = m == "" ? null : m;
}
The CellPainting now looks like this:
private void customDGV1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.ColumnIndex < 0 || e.RowIndex < 0) return;
DataGridViewCell cell = ((DataGridView)sender)[e.ColumnIndex, e.RowIndex];
if (cell.Tag == null) return;
string hide = cell.Tag.ToString();
if (hide.Contains("R"))
e.AdvancedBorderStyle.Right = DataGridViewAdvancedCellBorderStyle.None;
else
e.AdvancedBorderStyle.Right = DataGridViewAdvancedCellBorderStyle.Single;
if (hide.Contains("B"))
e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None;
else
e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.Single;
}
Update 2:
If you want to apply this to the ColumnHeaders you need to turn off dgv.EnableHeadersViualStyles first..
I want to change the focus on my grid cell.
Suppose I click on a cell[1,1] then I want to set the focus on cell[1,2].
Where cell[1,1] means cell of (column 1) and (row 1)
You could use the AfterCellActivate event and write this code
void grid_AfterCellActivate(object sender, EventArgs e)
{
if (grid.ActiveCell != null &&
grid.ActiveCell.Column.Index == 1 &&
grid.ActiveCell.Row.Index == 1)
{
grid.ActiveCell = grid.Rows[1].Cells[2];
// And if you want also to automatically
// put your cell in edit mode add this line
grid.PerformAction(UltraGridAction.EnterEditMode);
}
}
I need to write a function which will set the color in TableLayoutPanel cells depending on some condition during running the program.
TableLayoutPanel is divided by 16x16. There is some condition at the start of the program. If the condition is true for a cell this sell must be painted blue color. For example:
private void start_Click(object sender, EventArgs e)
{
foreach (string str in some_list)
{
if (some condition)
{
set_color_in_cell at row[i] colum[j] //(what shoud i use here?)
}
}
}
I found such example:
private void tableLayoutPanel_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
{
if (e.Row == 0 && e.Column == 1)
{
e.Graphics.FillRectangle(new SolidBrush(Color.Black), e.CellBounds);
}
}
But I don't understand how to use it. If somebody knows about this please help me.
private void start_Click(object sender, EventArgs e)
{
string SyncAnswer = "";
foreach (string file_string in Data_from_file)
{
COM_Port.WriteLine(file_string);
while (SyncAnswer != "READY")
{
SyncAnswer = COM_Port.ReadLine();
if (SyncAnswer.Substring(0, 4) == "Fire")
{
//raise event
//paint for example a cell in Row=i Colum=j
}
else if (SyncAnswer.Substring(0, 4) == "Skip")
{
//raise event
}
}
}
}
Option 1 - Using CellPaint Event
Here is a step by step example:
Create a Form
Put a TableLayoutPanel from toolbox on your Form
Select tableLayoutPanel1 on design surface and Press F4 Key to see properties.
From toolbar of property grid, you can select to show Properties or Events . Click on events icon and from the list, double click on CellPaint event to create tableLayoutPanel1_CellPaint event handler in code.
You can paint each cells background in this method based on some criteria. The event will raise for painting each cells background and e.Row is the row index, e.Column is column index and e.CellBounds is bound of the painting cell.
For example in below sample, we draw black background if ((e.Column + e.Row) % 2 == 1) otherwise, we draw white background:
private void tableLayoutPanel1_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
{
if ((e.Column + e.Row) % 2 == 1)
e.Graphics.FillRectangle(Brushes.Black, e.CellBounds);
else
e.Graphics.FillRectangle(Brushes.White, e.CellBounds);
}
To change Color Dynamically
To change the color from another point of program, for example in a Click event of a button, you should store back colors of each cell in an 2-dimension array and use that color to create a brush for that cell:
Define bgColors in your form:
Color[,] bgColors = new Color[2, 2] {
{ SystemColors.Control, SystemColors.Control },
{ SystemColors.Control, SystemColors.Control }
};
Draw background of cells this way:
private void tableLayoutPanel1_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
{
using (var b = new SolidBrush(bgColors[e.Column, e.Row]))
{
e.Graphics.FillRectangle(b , e.CellBounds);
}
}
To change the BackColor of a Cell you can:
private void Button1_Click(object sender, EventArgs e)
{
//column: 0 ,row: 1
bgColors[0, 1] = Color.Red;
tableLayoutPanel1.Refresh();
}
Option 2 - Hosting Panel in Cells
As another simple option, you can put Panel in each cell, and set the Dock property of Panel to Fill and set its Margin property to 0,0, then each time you want to change color of a panel at position (column, row) you can use this code:
this.tableLayoutPanel1.GetControlFromPosition(column, row).BackColor = Color.Red;
Another way that is pretty simple is to dynamically add multiple PictureBox controls (ex: pbCellColor below) to you cells in your TableLayoutPanel, or some other more simple simple control, dock it, set the margin to
zero, then whenever you want:
pbCellColor.Dock = DockStyle.None;
pbCellColor.Margin = new Size(0, 0, 0, 0);
pbCellColor.Backcolor = Color.Red;
Easy, no event handling or state checks. Just set it and forget it. Worst case, if you call from a non-gui thread, you will need to Invoke the action.