I have this code on RowDataBound that alternates the color of the cells and also add an onclick event on the cells. The data comes dynamically from a stored procedure so the gridview has only one control. The ButtonField.
I want to have white color for cells that hasn’t any data inside, how can I achieve this?
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
{
string back1 = "url('Images/GridBack1.jpg')";
string back2 = "url('Images/GridBack2.jpg')";
if (e.Row.RowType != DataControlRowType.DataRow)
return;
for (int i = 0; i < e.Row.Cells.Count; i++)
{
TableCell Cell = e.Row.Cells[i];
// if both row and column are odd, color them black
// if both row and column are even, color them white
if (((e.Row.RowIndex % 2 == 1) && (i % 2 == 1)) ||
((e.Row.RowIndex % 2 == 0) && (i % 2 == 0)))
{
if (string.IsNullOrEmpty(e.Row.Cells[i].Text)) //Edit
{
e.Row.Cells[i].BackColor = Color.Blue;
e.Row.Cells[i].ForeColor = Color.White;
}
else
{
e.Row.Cells[i].Width = Unit.Pixel(450);
e.Row.Cells[i].Height = Unit.Pixel(67);
e.Row.Cells[i].Style.Add("background-image", back1);
//e.Row.Cells[i].Style.Add("width", "150px");
//e.Row.Cells[i].Style.Add("word-wrap","break-word");
e.Row.Cells[i].Attributes.Add("align", "center");
}
}
else
{
if (string.IsNullOrEmpty(e.Row.Cells[i].Text)) //Edit
{
e.Row.Cells[i].BackColor = Color.Blue;
e.Row.Cells[i].ForeColor = Color.White;
}
else
{
e.Row.Cells[i].Width = Unit.Pixel(450);
e.Row.Cells[i].Height = Unit.Pixel(67);
e.Row.Cells[i].Style.Add("background-image", back2);
//e.Row.Cells[i].Style.Add("width", "150px");
//e.Row.Cells[i].Style.Add("word-wrap", "break-word");
e.Row.Cells[i].Attributes.Add("align", "center");
}
}
string color = "#000000";
e.Row.Cells[0].Attributes.Add("Style", "background-color: " + color + ";");
e.Row.Cells[0].Width = Unit.Pixel(150);
e.Row.Cells[0].Height = Unit.Pixel(67);
}
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton _singleClickButton = (LinkButton)e.Row.Cells[0].Controls[0];
string _jsSingle = ClientScript.GetPostBackClientHyperlink(_singleClickButton, "");
// Add events to each editable cell
for (int columnIndex = 2; columnIndex < e.Row.Cells.Count; columnIndex++)
{
// Add the column index as the event argument parameter
string js = _jsSingle.Insert(_jsSingle.Length - 2, columnIndex.ToString());
// Add this javascript to the onclick Attribute of the cell
e.Row.Cells[columnIndex].Attributes["onclick"] = js;
// Add a cursor style to the cells
e.Row.Cells[columnIndex].Attributes["style"] += "cursor:pointer;cursor:hand;";
}
}
}
}
Try to assign white color to the every cell and then overwrite according to conditions.
You need to use string.IsNullOrEmpty() like
if(string.IsNullOrEmpty(e.Row.Cells[i].Text))
{
e.Row.Cells[i].BackColor = Color.Blue;
e.Row.Cells[i].ForeColor = Color.White;
}
MSDN
Hope it works.
Related
I am designing a c# datagridview in which the enter key works as tab , back key results in moving back to previous cell . Also if the focus is at the last cell of last row of last column it creates a new row .
private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
int iColumn = dataGridView1.CurrentCell.ColumnIndex;
int iRow = dataGridView1.CurrentCell.RowIndex;
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Right || e.KeyCode == Keys.Tab)
{
//to check if the pointer reach last column
if (iColumn == dataGridView1.ColumnCount - 1)
{
//executing when line ends
if (dataGridView1.RowCount > (iRow + 1))
{
dataGridView1.CurrentCell = dataGridView1[0, iRow + 1];
}
// fires when you reach last cell of last row and last column
else
{
dataGridView1.Rows.Add();
dataGridView1.CurrentCell = dataGridView1[0, iRow +1];
}
}
else
{
dataGridView1.CurrentCell = dataGridView1[iColumn + 1, iRow];
}
}
if (e.KeyCode == Keys.Down)
{
int c = dataGridView1.CurrentCell.ColumnIndex;
int r = dataGridView1.CurrentCell.RowIndex;
if (r < dataGridView1.Rows.Count - 1) //check for index out of range
dataGridView1.CurrentCell = dataGridView1[c, r + 1];
}
if (e.KeyCode == Keys.Up)
{
int c = dataGridView1.CurrentCell.ColumnIndex;
int r = dataGridView1.CurrentCell.RowIndex;
if (r > 0) //check for index out of range
dataGridView1.CurrentCell = dataGridView1[c, r - 1];
}
if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Left)
{
//to check if you are in first cell of any line
if (iColumn == 0)
{
//to check if you are in first cell of fist line
if (iRow == 0)
{
MessageBox.Show("you reached first cell");
}
else
{
dataGridView1.CurrentCell = dataGridView1[dataGridView1.ColumnCount - 1, iRow - 1];
}
}
else
{
dataGridView1.CurrentCell = dataGridView1[iColumn - 1, iRow];
}
}
}
Now , after this i want to edit the cell values and want to move to next cell when i press enter key , so i add the following event (as i am in cell editing mode due to this, the above key down event will not raise )
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
// MOVE TO NEXT CELL WHEN DONE EDITING
SendKeys.Send("{right}");
}
But after using the above event cellendedit , i found out that the cell is moving down to the next row(default behavior of datagridview) and then to next row's next cell(due to cellendedit) . Instead it should move to the next column same row . Please suggest me to fix this behaviour .
Your question is how to suppress the default DataGridView navigation and substitute your own. The default navigation occurs in the OnKeyDown method, and if you override it in your custom control (let's call it DataGridViewEx) you should be able to suppress the default navigation by overriding that method, making sure that you do not call the base class version for Keys.Enter and Keys.Back.
BindingList<Record> Records = new BindingList<Record>();
protected override void OnKeyDown(KeyEventArgs e)
{
if (CurrentCell != null)
{
var row = CurrentCell.RowIndex;
var col = CurrentCell.ColumnIndex;
switch (e.KeyData)
{
case Keys.Return:
col++;
// [ToDo] Allow for non-visible rows (will fail).
if(col.Equals(Columns.Count))
{
col = 0;
row++;
// Either insert after the current row as
// shown here, or add to the end of the list.
Records.Insert(row, new Record());
}
CurrentCell = this[col, row];
break;
case Keys.Back:
col--;
if(col >= 0)
{
CurrentCell = this[col, row];
}
break;
default:
base.OnKeyDown(e);
break;
}
}
}
TESTBENCH
Here's how I tested this solution after changing the two entries in MainForm.Designer.cs to use the DataGridViewEx:
namespace current_cell_focus
{
public partial class MainForm : Form
{
public MainForm() => InitializeComponent();
}
// Custom control
class DataGridViewEx : DataGridView
{
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
AllowUserToAddRows = false;
DataSource = Records;
// Format columns
Records.Add(new Record());
foreach (DataGridViewColumn col in Columns)
{
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
Records.Clear();
// Load test data
Records.Add(new Record
{
Column1 = "1",
Column2 = "2",
Column3 = "3"
});
Records.Add(new Record
{
Column1 = "4",
Column2 = "5",
Column3 = "6"
});
Records.Add(new Record
{
Column1 = "7",
Column2 = "8",
Column3 = "9"
});
}
BindingList<Record> Records = new BindingList<Record>();
protected override void OnKeyDown(KeyEventArgs e)
{
if (CurrentCell != null)
{
var row = CurrentCell.RowIndex;
var col = CurrentCell.ColumnIndex;
switch (e.KeyData)
{
case Keys.Return:
col++;
// [ToDo] Allow for non-visible rows (will fail).
if(col.Equals(Columns.Count))
{
col = 0;
row++;
// Either insert after the current row as
// shown here, or add to the end of the list.
Records.Insert(row, new Record());
}
CurrentCell = this[col, row];
break;
case Keys.Back:
col--;
if(col >= 0)
{
CurrentCell = this[col, row];
}
break;
default:
base.OnKeyDown(e);
break;
}
}
}
}
class Record
{
public string Column1 { get; set; }
public string Column2 { get; set; }
public string Column3 { get; set; }
}
}
How do I highlight a datagrid row based off the value in textbox2?
Ultimately when a matching value is found in Column 2 the QTY field (column 3) on that corresponding row will need change by -1 for each QR Code scanned by the End user. Once the QTY value reaches 0 the the row will need to be highlighted Green.
I can't just get it to work have tried a few different ways of writing the foreach section but no luck
My code is as below:
private void textBox2_KeyPress(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
iCBOMHDataGridView.DataSource = iCBOMHBindingSource;
string input = textBox2.Text;
string output = "";
textBox2.Text = Regex.Replace(input, #"^\d{4}|[A-z]{2}[0-9]{5},|,|,|\d{|[0-9]{4}/....|\d{1,}\/\d{2,2}\/\d{4}|\s.........|\s|,|,|,|\d*?.$|[*?:/]\n|\r|\r\n", output);
foreach (DataGridViewRow row in iCBOMHDataGridView.Rows)
{
if ((string)row.Cells[2].Value == textBox2.Text)
{
row.Selected = true;
}
else
{
row.Selected = false;
MessageBox.Show("Part Number doesn't match");
}
}
}
}
You can do a loop inside all cells of that row and set the cell Style property. You can create different styles for non-selected and selected rows, then apply these styles as necessary.
Example:
DataGridViewCellStyle selectedStyle = new DataGridViewCellStyle();
selectedStyle.BackColor = Color.LemonChiffon;
selectedStyle.ForeColor = Color.OrangeRed;
DataGridViewCellStyle defaultStyle = new DataGridViewCellStyle();
defaultStyle.BackColor = System.Drawing.Color.White;
defaultStyle.ForeColor = Control.DefaultForeColor;
foreach (DataGridViewRow row in iCBOMHDataGridView.Rows)
{
if ((string)row.Cells[2].Value == textBox2.Text)
{
row.Selected = true;
if(Decimal.Parse(row.Cells[3].Value.ToString()) > 0)
row.Cells[2].Value = Decimal.Parse(row.Cells[2].Value.ToString()) - 1;
}
if (Decimal.Parse(row.Cells[3].Value.ToString()) <= 0)
{
foreach (DataGridViewCell col in row.Cells.AsParallel())
col.Style = selectedStyle;
}
else
{
foreach (DataGridViewCell col in row.Cells.AsParallel())
col.Style = defaultStyle;
}
}
If you do not want to loop through the cells, you can simply change the DataRow.DefaultCellStyle property of each row. But this will limit your customization options.
What I am doing is comparing two datatables checking for changes. If a table cell value changes I mark the corresponding DataGridView cell by changing its BackColor property. This has worked perfectly until I needed to set the DataGridView.DefaultCellStyle.WrapMode = DataGridViewTriState.True and DataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells. I needed to do this so I could have text go to a new line inside of a cell. Here is a StackOverflow answer which explains this. So after setting a DefaultCellStyle I was unable to change the BackColor. However I have a Validator class which gets a DataGridViewCell passed in and validates the value. If the text in the cell is not valid or in correct format the Style.BackColor gets changed...THIS WORKS! My question is why can my Validator class change the BackColor but I can't directly from the form? Here is a simplified example of what I am doing and what I am experiencing:
private void initDataGridView()
{
DataGridView.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
DataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
DataGridView.MultiSelect = false;
DataGridView.AllowUserToAddRows = false;
DataGridView.AllowUserToDeleteRows = false;
DataGridView.RowHeadersVisible = false;
// ADD EVENT HANDLERS
DataGridView.CellEndEdit += DataGridView_CellEndEdit;
}
private void compareTables()
{
string dataTableCell;
string dataTableMirrorCell;
for (int rowIndex = 0; rowIndex < dataTable.Rows.Count; rowIndex++)
{
for (int colIndex = 2; colIndex < dataTable.Columns.Count; colIndex++)
{
// initialize cell values to compare
dataTableCell = dataTable.Rows[rowIndex].Field<string>(colIndex);
dataTableMirrorCell = dataTableMirror.Rows[rowIndex].Field<string>(colIndex);
// now compare cell values
if (dataTableCell != dataTableMirrorCell)
{
if (dataTableCell== null
|| dataTableCell== string.Empty)
{
dataGridView.Rows[rowIndex].Cells[colIndex].Style.BackColor = Color.Red;
dataGridView.Rows[rowIndex].Cells[colIndex].Tag = "Delete";
}
else
{
if (dataTableMirrorCell == null
|| dataTableMirrorCell == string.Empty)
{
dataGridView.Rows[rowIndex].Cells[colIndex].Style.BackColor = Color.Orange;
dataGridView.Rows[rowIndex].Cells[colIndex].Tag = "Add";
}
else
{
dataGridView.Rows[rowIndex].Cells[colIndex].Style.BackColor = Color.Yellow;
dataGridView.Rows[rowIndex].Cells[colIndex].Tag = "Change";
}
}
}
}
}
}
private void dataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
Validator validator = new Validator(this);
if (e.ColumnIndex != dgv.Columns["Name"].Index)
{
DataGridViewCell cell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
// Check the value for valid format
validator.validateCell(cell, ValidationType.Numeric);
}
}
Here is the Validator.validateCell method:
public void validateCell(DataGridViewCell cell, String type)
{
// NUMERIC
if (type.ToLower() == "numeric")
{
int result;
if (!int.TryParse(cell.Value.ToString(), out result))
{
cell.Style.BackColor = Color.Beige;
cell.Style.Font = new Font(this.form.Font, FontStyle.Bold);
cell.ToolTipText = "This is a " + type + " only field.";
cell.Tag = "invalid";
}
else
{
cell.Style.BackColor = Color.White;
cell.Style.Font = new Font(this.form.Font, FontStyle.Regular);
cell.ToolTipText = string.Empty;
cell.Tag = "valid";
}
}
}
Validator.validateCell WORKS, it changes the cell BackColor and the Font. Can someone explain why a local method, compareTables, does not change BackColor and my validator.validateCell does? Many thanks for helping me learn!
I have always used:
dataGridView.RowsDefaultCellStyle.SelectionBackColor = Color.Red;
To set or get a dataGridView background color. Not sure if that will help you look in another direction
I've got an interesting issue. I am trying to use a datatable as a data source for a datagridview. I want to color some of the cells of the table to indicate various things, but for some reason the color will not display. So the following code shows an uncolored cell.
dataGridView1.DataSource = table;
dataGridView1.Rows[0].Cells[0].Style.BackColor = Color.Yellow;
I can only get a color to display after the initial form load (for example setting a cell color on the OnClick event). However, if I explicitly create the rows and columns for the view as in the code below, the coloring works.
foreach (DataColumn col in table.Columns)
dataGridView1.Columns.Add(col.ColumnName, col.ColumnName);
for (int i = 0; i < table.Rows.Count; i++)
{
var row = table.Rows[i];
object[] values = new object[table.Columns.Count];
for (int x = 0; x < table.Columns.Count; x++)
values[x] = row[x].ToString();
dataGridView1.Rows.Add(values);
}
dataGridView1.Rows[0].Cells[0].Style.BackColor = Color.Yellow;
I do not want to have the code in this manner. Does anyone know what is happening here that is preventing me from coloring the cells?
If you try and set the cell colour within the constructor of the form you will be hitting before the data binding is completed so the changes to the cells don't stick (don't ask me why, just one of those gotchas with the DataGridView.
The most straightforward fix to this is to set the colours a little later - usually within a DataBindingComplete event handler:
void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
dataGridView1.Rows[0].Cells[0].Style.BackColor = Color.Yellow;
}
This is appropriate for static colouring of the grid - if you want the colours to change according to the changes within the grid then use the CellFormatting event to change the cells.
This Is something I have implemented recently, I dont know if it will help??
private void dgvOutstandingReports_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
int colIndex = e.ColumnIndex;
int rowIndex = e.RowIndex;
if (rowIndex >= 0 && colIndex >= 0)
{
DataGridViewRow theRow = dgvOutstandingReports.Rows[rowIndex];
if (theRow.Cells[colIndex].Value.ToString() == "Daily Report")
{
theRow.DefaultCellStyle.BackColor = Color.LightYellow;
}
else if (theRow.Cells[colIndex].Value.ToString() == "Monthly Report")
{
theRow.DefaultCellStyle.BackColor = Color.LightGray;
}
else if (theRow.Cells[colIndex].Value.ToString() == "SMP Report")
{
theRow.DefaultCellStyle.BackColor = Color.Snow;
}
else if (theRow.Cells[colIndex].Value.ToString() == "Weekly Report")
{
theRow.DefaultCellStyle.BackColor = Color.Pink;
}
else if (theRow.Cells[colIndex].Value.ToString() == "Hourly Report")
{
theRow.DefaultCellStyle.BackColor = Color.LightSteelBlue;
}
}
}
After posting this: Custom Header in GridView
...I have a related problem. I have added the table row during OnDataBound, and it shows up, the links are clickable. There are two problems with adding it here: first, if a postback occurs that doesn't DataBind, the row disappears; second, no events are happening when the LinkButtons are clicked. Here is the OnDataBound code:
protected override void OnDataBound(EventArgs e)
{
base.OnDataBound(e);
// Hook up the handler to create the Selection header/footer
// TODO: Wrap this in a function sometime
Table table = (Table)Controls[0];
GridViewRow row = new GridViewRow(-1, -1, DataControlRowType.EmptyDataRow, DataControlRowState.Normal);
// TODO: should add css classes
TableHeaderCell cell = new TableHeaderCell();
cell.ColumnSpan = Columns.Count + 1; // plus 1 for the checkbox column
cell.HorizontalAlign = HorizontalAlign.Left; // do this or css?
HtmlGenericControl label = new HtmlGenericControl("label");
label.InnerText = "Select:";
selectNoneLK = new LinkButton();
selectNoneLK.ID = "SelectNoneLK";
selectNoneLK.Text = "None";
selectNoneLK.Click += SelectNoneLK_Click;
//selectNoneLK.CommandName = "SelectNone";
//selectNoneLK.Command += SelectNoneLK_Click;
selectAllLK = new LinkButton();
selectAllLK.ID = "SelectAllLK";
selectAllLK.Text = "All on this page";
//selectAllLK.CommandName = "SelectAll";
//selectAllLK.Command += SelectAllLK_Click;
selectAllLK.Click += SelectAllLK_Click;
cell.Controls.Add(label);
cell.Controls.Add(selectNoneLK);
cell.Controls.Add(selectAllLK);
row.Controls.Add(cell);
// Find out where to put this row
int rowIndex = 0;
if(SelectionMode == SelectionMode.AboveHeader)
{
rowIndex = 0;
}
else if(SelectionMode == SelectionMode.BelowHeader)
{
rowIndex = 1;
}
else if(SelectionMode == SelectionMode.AboveFooter)
{
rowIndex = table.Rows.Count;
}
else if(SelectionMode == SelectionMode.BelowFooter)
{
rowIndex = table.Rows.Count + 1;
}
table.Rows.AddAt(rowIndex, row);
}
You can try putting it in the RowCreated Event, while the header is being created. This might also fix your problem with the LinkButtons not working.
void GridView1_RowCreated(Object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.Header)
{
...your code here
}