Forcing Gridview to display x number of rows including empty rows - c#

Is it possible to force gridview to display x number of rows even if the data is less than x rows? With the difference being made up with empty rows, of course.
I found this page: http://aspdotnetcodebook.blogspot.ca/2008/03/how-to-force-force-x-number-of-rows-in.html while trying to google the issue but there's not much explanation on how to use the presented solution.
Thanks,

Before binding your DataSource to your GirdView, I would check the number of rows returned and add empty rows to your data source.
So let's say you want always 10 rows Visible:
var myDataSource = GetDataSource();
if(myDataSource.Count() < MIN_NUMBER_OF_ROWS)
{
myDataSource.AddRange(GetEmptyRows(MIN_NUMBER_OF_ROWS - myDataSource.Count()));
}
myGridView.DataSource = myDataSource;
And then GetEmptyRows(int numberOfRowsNeeded) returns the number of empty rows you need.
EDIT: Let's say your source is of type MyCustomGridRow with attribute isValid. You can then intercept each row on data binding and according to the isValid attribute, modify the appearance of your GridViewRow (Custom message, colspan, ...).
protected virtual void myGridView_OnRowDataBound(GridViewRowEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
MyCustomGridRow customRow = (MyCustomGridRow)(e.Item.DataItem);
if (!customRow.isValid)
{
int colCount = myGridView.Columns.Count;
e.Item.Cells.Clear();
Label lblEmptyMessage = new Label
{
Text = "Custom message for eempty rows.",
CssClass = "ErrLabels"
};
TableCell newCell = new TableCell
{
ColumnSpan = colCount
};
newCell.Controls.Add((Control)lblEmptyMessage);
e.Item.Cells.Add(newCell);
}
}
}

The following code does what I was looking to do:
DataTable dt1 = new DataTable();
DataRow dr1;
dt1.Columns.Add("ProjectID");
dt1.Columns.Add("ProjectName");
dt1.Columns.Add("Country");
for (int i = 0; i < 10; i++)
{
dr1 = dt1.NewRow();
dr1["ProjectID"] = dr1["ProjectName"] = dr1["Country"] = "";
dt1.Rows.Add(dr1);
}
dt1.AcceptChanges();
ProjectListGridView.DataSource = dt1;
ProjectListGridView.DataBind();

Related

Looping through gridview and change certain column font colour

So I have a gridview and I wanted to make certain columns text a different colour... i.e every column that says actual I want this text to be green... can anybody help? My gridlooks similar to this.
Hour - actual A - target A - actual aa - target aa - actual b - target b.
And finally is there a way to reset the data in my gridview after a certain amount of time... i.e shiftstart 6am-2pm 2pm-10pm 10pm-6am... So the data refreshes after 8 hours back to zero.
public void Refreshdata(int selectedProduct, DateTime shiftStart, DateTime shiftEnd)
{
BizManager biz = new BizManager();
GridView1.DataSource = biz.GetPacktstatisticsForShift(
shiftStart
, shiftEnd
, selectedProduct).DefaultView;
GridView1.DataBind();
public DataTable CreatePackingStats(DataSet dset)
{
using (DataManager dmgr = new DataManager())
{
DataTable target = dset.Tables[0];
DataTable actual = dset.Tables[1];
DataColumn[] cols = new DataColumn[1];
cols[0] = actual.Columns["Hour"];
actual.PrimaryKey = cols;
DataTable final = new DataTable();
// Create table columns
foreach (DataColumn col in target.Columns)
{
final.Columns.Add(new DataColumn(col.ColumnName, col.DataType));
if (col.ColumnName.Contains("Target"))
{
// Add an equivilant actual column
string newColumnName = col.ColumnName.Replace("Target", "Actual");
final.Columns.Add(newColumnName, col.DataType);
}
}
//// Add rows to new table
foreach (DataRow row in target.Rows)
{
string key = row["Hour"].ToString();
DataRow newRow = final.Rows.Add();
// Store column value
foreach (DataColumn col in final.Columns)
{
if (col.ColumnName.Contains("HOUR") || col.ColumnName.Contains("Target"))
{
newRow[col.ColumnName] = row[col.ColumnName];
}
else
{
// Find actual data
DataColumn actColumn = actual.Columns[col.ColumnName] as DataColumn;
if (actColumn == null)
{
newRow[col.ColumnName] = 0;
}
else
{
if (string.IsNullOrEmpty(actual.Rows.Find(key)[col.ColumnName].ToString()))
{
newRow[col.ColumnName] = 0;
}
else
{
newRow[col.ColumnName] = actual.Rows.Find(key)[col.ColumnName].ToString();
}
}
}
}
}
return final;
The CreatePackingStats is populating my grid with added columns FYI.
I guess there is a way to add colour text whilst the code is looping through the data and creating extra columns, not sure how to do this tho.?
And also the CreatePackingStats is located in a class and not in the page behind aspx.
Sorry about all the questions I am new and learning, all your help will help to develop and I appreciate all the help I receive.
Right-click on your GridView then go to the properties tab and select events.In there you will find the event called RowDataBound.
In that event write your code to change the forecolor like:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//here the Cells is an array where you can pass the index value of the cell where you want to check and if you don't know where the value is then you can do a for loop and then check the value
if (e.Row.Cells[0].Text == "someValue")
{
e.Row.Cells[0].ForeColor = System.Drawing.Color.Red;
}
}
}
Update 1 for comparing the value using the IndexOf()
As for the data what you have given, you have to change the compare function from == to IndexOf("SomeValue").For that, you can try the IndexOf("actual"). If it gives value > -1 then change the color.
or you can try the below code where I am looping through all the columns in the row(you can try to avoid the looping if you have knowledge on which column the value will occur):
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
for (int i = 0; i < e.Row.Cells.Count; i++)
{
if (e.Row.Cells[i].Text.ToLower().IndexOf("actual") > -1)
{
e.Row.Cells[i].ForeColor = System.Drawing.Color.Red;
}
}
}
}
Update 2 Adding the snapshots of sample data and it's output.
Here is the sample data with which I am working:
And here is the processed output using the IndexOf() loop over the in RowDataBound event.
Hope this helps.

Unable to set Width of Column in GridView ASP.NET (No DataSource for GridView)

An error popped up upon running the application.
I'm trying to set the Width of the First Column of my GridView but i can't do it.
Rows, Columns, Data of this GridView is not bounded to any DataSource.
//By Class Statistics
int A1Available = get.countAvailA1();
int A1Alloted = get.countUnavailA1();
int B1Available = get.countAvailB1();
int B1Alloted = get.countUnavailB1();
int B2Available = get.countAvailB2();
int B2Alloted = get.countUnavailB2();
int C1Available = get.countAvailC1();
int C1Alloted = get.countUnavailC1();
DataTable dtClass = new DataTable();
dtClass.Columns.Add("Class");
dtClass.Columns.Add("A1");
dtClass.Columns.Add("B1");
dtClass.Columns.Add("B2");
dtClass.Columns.Add("C1");
DataRow r;
r = dtClass.NewRow();
r["Class"] = "Number of Available Beds";
r["A1"] = A1Available.ToString();
r["B1"] = B1Available.ToString();
r["B2"] = B2Available.ToString();
r["C1"] = C1Available.ToString();
dtClass.Rows.Add(r);
r = dtClass.NewRow();
r["Class"] = "Number of Unavailable Beds";
r["A1"] = A1Alloted.ToString();
r["B1"] = B1Alloted.ToString();
r["B2"] = B2Alloted.ToString();
r["C1"] = C1Alloted.ToString();
dtClass.Rows.Add(r);
bedStats.DataSource = dtClass;
bedStats.DataBind();
bedStats.Columns[1].HeaderStyle.Width = new Unit(55, UnitType.Percentage);
Used this code to set the Width. Is there any other way to? Doesn't have be bothered about the value, just setting the Width..
bedStats.Columns[1].HeaderStyle.Width = new Unit(55, UnitType.Percentage);
Image of Error
Setting column values will only work with TemplateField and BoundField columns. Autogenerated columns are not part the column collection in the GridView. If you want to color the Headers you need to use the OnRowDataBound event. Only then you can access the columns.
protected void bedStats_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
e.Row.Cells[1].Width = new Unit(55, UnitType.Percentage);
e.Row.Cells[1].BackColor = Color.Pink;
}
}

Setting Value on DataGridViewComboBox column very slow

I have a DataGridView that I add a DataGridViewComboBoxColumn to, add values, and set the Value by iterating through the grid rows. The selected Value is the value from another row cell. This is very slow and I'm not sure if I am using best practice or not. For a grid with 3000 rows this takes over two minutes to complete. I have tried iterating via row count and using a foreach loop (in commented code below) with same results. Is there a faster way to do this? Here is my code:
DataGridViewComboBoxColumn cmb = new DataGridViewComboBoxColumn();
cmb.HeaderText = "New Class";
cmb.Name = "cmb";
foreach (DataGridViewRow row in dgClasses.Rows)
{
if (row.Cells[0].Value != null)
{
cmb.Items.Add(row.Cells[0].Value);
}
}
dgProducts.Columns.Add(cmb);
for (int i = 0; i < dgProducts.Rows.Count; i++)
{
dgProducts.Rows[i].Cells["cmb"].Value = dgProducts.Rows[i].Cells["Class"].Value;
}
//foreach (DataGridViewRow row in dgProducts.Rows)
//{
// row.Cells["cmb"].Value = row.Cells["Class"].Value;
//}
I added DataPropertyName and got rid of the looping through the grid rows. Loads very fast now.
private void AddClassCombobox()
{
DataGridViewComboBoxColumn cmb = new DataGridViewComboBoxColumn();
cmb.HeaderText = "New Class";
cmb.Name = "cmb";
cmb.DataPropertyName = "Class"; // << Added this
foreach (DataGridViewRow row in dgClasses.Rows)
{
if (row.Cells[0].Value != null)
{
cmb.Items.Add(row.Cells[0].Value);
}
}
dgProducts.Columns.Add(cmb);
}

Reset column increment datagridview

I am trying to update the row increment number in a DataGridView in my WinForms application after deleting a row or rows. I have looked at sources and the all point on how to add the incrementing to a column in DataTable. My DataGridView is bound to my DataTable, and that is bound to a DataSet.
How I created by datatable:
DataColumn itemNumber = new DataColumn();
itemNumber.ColumnName = "ItemNumber";
itemNumber.AutoIncrement = true;
itemNumber.AutoIncrementSeed = 1;
itemNumber.AutoIncrementStep = 1;
DataColumn article = new DataColumn();
article.ColumnName = "Article";
article.ReadOnly = true;
DataColumn description = new DataColumn();
description.ColumnName = "Description";
description.ReadOnly = true;
DataColumn type = new DataColumn();
type.ColumnName = "Type";
type.ReadOnly = true;
//add to datatable
dt.Columns.Add(itemNumber);
dt.Columns.Add(article);
dt.Columns.Add(description);
dt.Columns.Add(type);
Removing a row from the DataGridView
foreach (DataGridViewRow row in dgvView.SelectedRows)
{
dgvView.Rows.RemoveAt(row.Index);
}
If I have 5 rows, and delete one. I would like the increment values to start from the 1,2,3,4 etc...
Can someone point me on how to achieve this?
I can't think of an efficient way to do this, but here are some ideas that may work with small data sets.
Keep up with the values yourself
1) Don't define the column as an AutoIncrement column):
DataColumn itemNumber = new DataColumn();
itemNumber.ColumnName = "ItemNumber";
//itemNumber.AutoIncrement = true;
//itemNumber.AutoIncrementSeed = 1;
//itemNumber.AutoIncrementStep = 1;
2) Handle the DataGridView.RowsAdded and DataGridView.RowsRemoved events and "reset" the values:
private void dgvView_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
if (dgvView.Columns.Contains("ItemNumber"))
{
foreach (DataGridViewRow r in dgvView.Rows)
{
r.Cells["ItemNumber"].Value = r.Index + 1;
}
}
}
private void dgvView_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
if (dgvView.Columns.Contains("ItemNumber"))
{
foreach (DataGridViewRow r in dgvView.Rows)
{
r.Cells["ItemNumber"].Value = r.Index + 1;
}
}
}
Regenerate the values by rebuilding the DataTable and rebinding to it
1) Define the helper method below:
private DataTable ResetAutoIncrementColumn(DataTable dt, string autoIncrementColumnName)
{
DataTable result = new DataTable();
DataColumn itemNumber = new DataColumn(autoIncrementColumnName);
itemNumber.AutoIncrement = true;
itemNumber.AutoIncrementSeed = 1;
itemNumber.AutoIncrementStep = 1;
result.Columns.Add(itemNumber);
dt.Columns.Remove(autoIncrementColumnName);
result.Merge(dt, true);
return result;
}
2) Call it at the appropriate time (e.g. after a series of deletes as in the original question):
dt = ResetAutoIncrementColumn(dt, "ItemNumber");
dt.Columns["ItemNumber"].SetOrdinal(0);
dgvView.DataSource = dt;
dgvView.Columns["ItemNumber"].DisplayIndex = 0;
If you are just looking for a visual row number
Also, if you are just looking to have a visual row number on the DataGridView (and you don't care about the value being present in the underlying DataTable), you can handle the DataGridView.RowPostPaint event as follows (pulled from https://stackoverflow.com/a/12840794/3085273):
private void dgGrid_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
var grid = sender as DataGridView;
var rowIdx = (e.RowIndex + 1).ToString();
var centerFormat = new StringFormat()
{
// right alignment might actually make more sense for numbers
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
var headerBounds = new Rectangle(e.RowBounds.Left, e.RowBounds.Top, grid.RowHeadersWidth, e.RowBounds.Height);
e.Graphics.DrawString(rowIdx, this.Font, SystemBrushes.ControlText, headerBounds, centerFormat);
}

Copy Data Table to a Data Grid without binding them together

I have two datagrids in my application (dataGridView1 and dataGridView2). I am moving selected items from dataGridView1 into dataGridView2. Here is how I am currently doing this:
DataTable dt = new DataTable("dt");
DataTable id = new DataTable("id");
try
{
if (dataGridView1.Rows.Count > 0)
{
for (int i = 0; i < dataGridView1.Rows.Count - 0; i++)
{
if (dataGridView1.Rows[i].Cells[0].Value != null)
{
DataRow row;
DataRow idRow;
row = dt.NewRow();
idRow = id.NewRow();
idRow["id"] = dataGridView1.Rows[i].Cells[1].Value.ToString();
row["id"] = dataGridView1.Rows[i].Cells[1].Value.ToString();
row["Link Name"] = dataGridView1.Rows[i].Cells[2].Value.ToString();
dt.Rows.Add(row);
id.Rows.Add(idRow);
}
}
dataGridView2.DataSource = dt;
}
However, I also need to be able to remove items from dataGridView2. Currently, the DataTable dt is bound to dataGridView2 and I cant clear it after the items are added because it also clears the dataGridView2.
Basicly, my question would be, is there a way to add the contents of a data table to a data grid without using datagrid.DataSource?
You can manually add each row using the dataGridView2.Rows.Add function. Also, I have typically used the dataGridView2.Row(n).Tag to hold the actual source of that row's data.
int n = dataGridView2.Rows.Add();
DataGridViewRow newRow = dataGridView2.Rows[n];
newRow.Cells[0].Value = "ABC";
newRow.Cells[1].Value = 123;
newRow.Tag = row;

Categories