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.
Related
I want to get rid of having negative values on the datagrid and I only want to show that once the item goes out of stock it will only show zero instead of -4
things to consider:
- I call my datagridview manually here:
void GetInventory()
{
using (SqlConnection con = new SqlConnection(Helper.GetConnection()))
{
con.Open();
string query = #"SELECT * FROM InventoryTable";
using (SqlCommand cmd = new SqlCommand(query, con))
{
using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
sda.Fill(dt);
dgvItem.DataSource = dt;
}
}
}
}
This shouldn't be handled in the sql query.
This is responsibility of the view logic. DataGridView is view's control and could be right place to convert quantity into "friendly" value.
DataGridView.CelLFormatting event handler could be right tool for the job.
// In constructor
dgvItem.CellFormatting += dgvItem_CellFormatting;
private void dgvItem_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
var dgv = sender as DataGridView;
if (dgv.Columns[e.ColumnIndex].Name != "Quantity")
{
return;
}
if (e.Value == null)
{
return;
}
var quantity = (decimal)e.Value;
if (quantity < 0)
{
e.Value = 0;
}
}
So you are having products that have minus stock but you do not want final user too see it but just to see 0 (which means out of stock).
There are many approach to this problem but let's say you cannot avoid getting into minus then you can filter your datagridview after populating it. Additional function = slower program so reconsider solving problem with not getting into minus.
So how it could be done is with extension.
You create it like this (i will put simple example):
public static void ReplaceAllNegativeWithZeros(this DataGridView dgv)
{
foreach(DataGridViewRow row in dgv.Rows)
{
foreach(DataGridViewCell cell in dgv.Cells)
{
//This will loop through all cells in your currently displayed datagridview
//You call this function like yourDataGridViewInForm.ReplaceAllNegativeWithZeros();
//Here check if value from cell meets your condition and then change it.
}
}
}
With this you can check all your cells (or just one column) and do with it's values whatever you need (replace them to 0 if < 0)
One way to do this is to have your query return a formatted or calculated value, like this for example
select case when i.quantity < 0 then 0 else i.quantity end as quantityZero,
i.*
from InventoryTable i
Now you can put the original quantity column invisible on your datagridview.
This way you have both the original value at hand should you need it, and a value that will show zero when < 0 to display
It is also best practice to not do select * but to always list the fields you need.
public static void ReplaceAllNegativeWithZeros(DataGridView dgv)
{
foreach (DataGridViewRow row in dgv.Rows)
{
foreach (DataGridViewCell cell in row.Cells)
{
if (cell.Value != null)
{
if (Convert.ToInt32(cell.Value.ToString()) < 0)
{
cell.Value = 0;
}
}
}
}
}
Can please any one help me on this. I have developed a c# windows application which has DataGridView first column has checkboxes. if I click on first column header it selects all the row level check boxes except the first row. For selecting all row level check boxes I have an event of dataGridView1_ColumnHeaderMouseClick and the code is:
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
foreach (DataGridViewColumn column in dataGridView1.Columns)
{
column.SortMode = DataGridViewColumnSortMode.NotSortable;
}
if (e.ColumnIndex == 0)
{
if (chek == 0)
{
try
{
for (int i = 0; i < dataGridView1.RowCount; i++)
{
string paymentValue = dataGridView1.Rows[i].Cells[18].Value.ToString();
string incmngp = dataGridView1.Rows[i].Cells[20].Value.ToString();
if (paymentValue == "N" && incmngp =="")
{
dataGridView1.Rows[i].Cells[0].Value = 1;
chek = 1;
}
}
if (chek == 1)
{
btn_update.Text = "Update";
}
}
catch (Exception ) { }
}
else if(chek==1)
{
try
{
for (int i = 0; i < dataGridView1.RowCount; i++)
{
dataGridView1.Rows[i].Cells[0].Value = 0;
chek = 0;
}
if (chek == 0)
{
btn_update.Text = "OK";
}
}
catch (Exception) { }
}
}
Note: chek is the variable declared on initialize stage
Set your Selection mode property of data grid view to ColumnHeaderSelect
and make sure all your 'Text' columns have SortMode set to NotSortable
UPDATE 2
In which case, Undo everything I ever told before and do that like this
Before you are assigning any DataTable to dataGridView1.
da.Fill(dt);
dataGridView1.DataSource = dt.DefaultView;
dataGridView1.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
foreach(DataGridViewColumn dc in dataGridView1.Columns)
{
dc.SortMode = DataGridViewColumnSortMode.NotSortable;
}
dataGridView1.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect;
UPDATE 3
Add an event handler for your dataGridView1's ColumnHeaderMouseClick Event
like below
Add the below code (Generic code if you want to use the same functionality for any column of check boxes)
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
//Enter your own column index here
if(e.ColumnIndex == 0)
foreach(DataGridViewRow row in dataGridView1.Rows)
foreach (DataGridViewCell cell in row.Cells)
{
//Check if the cell type is of a CheckBoxCell
if (cell.GetType() == typeof(DataGridViewCheckBoxCell))
{
DataGridViewCheckBoxCell c = (DataGridViewCheckBoxCell)cell;
c.TrueValue = "T";
c.FalseValue = "F";
if (c.Value == c.FalseValue|| c.Value == null )
c.Value = c.TrueValue;
else
c.Value = c.FalseValue;
}
}
dataGridView1.RefreshEdit();
}
This is a very bizarre bug in Winforms. The problem more generally applies not to the first row, but to the first selected cell in any row of DataGridViewCheckBoxCell(s). You can select the CheckBox cell by clicking on the check box, or select the cell outside the check box, the behavior is the same. If you select 3 check boxes in the middle of your grid, the first of those three will freeze and not update properly. If you try to clear the selection in code, with a dataGridView1.ClearSelection() method call, it still does not work.
The correct answer is to call datagridview1.RefreshEdit() right after you change the checkbox data. You can't just call it after all changes are made. It must be made for each change in the CheckBox value.
foreach (DataGridViewRow row in Results.Rows)
{
var ck = (DataGridViewCheckBoxCell) row.Cells["check"];
ck.Value = ck.TrueValue;
Results.RefreshEdit();
}
I would like to iterate through a datatable of rows using Next and Previous buttons.
My form has the following:
[Ticket Ref]
[Short Description]
[Next Step]
[Last Updated]
Through the life of a case, you might get multiple updates. Therefore I may have 5 rows in the database relating to a particular Ticket Reference. Comments and Last Updated will obviously be different.
When the form loads it will display the last record in the database into the text boxes i.e the last row from the query. I would then like to click Previous and see the previous rows. But if i click on Next I expect it to loop through. i.e go to the next record. So if im on record 1, then it needs to go to 2.
I have tried counting the clicks but this isnt very helpful as when the form first loads count will be 0 and if i click Previous then i will get a Row out of position -1 error.
My previous button looks like this:
protected void btnPrevious_Click1(object sender, EventArgs e)
{
DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter eobj = new DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter();
DataTable dt = new DataTable();
dt = eobj.GetTicketUpdates(txtSupportRef.Text);
int i = 0;
if (i < dt.Rows[0].Table.Rows.Count - 1 || i != 0)
{
i--;
txtShortDesc.Text = dt.Rows[0].Table.Rows[i]["ShortDesc"].ToString();
txtNextStep.Text = dt.Rows[0].Table.Rows[i]["NextStep"].ToString();
txtLastUpdated.Text = dt.Rows[0].Table.Rows[i]["LastUpdated"].ToString();
}
else
{
//no records to see more.
}
}
My next button looks like this:
protected void btnNext_Click1(object sender, EventArgs e)
{
DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter eobj = new DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter();
DataTable dt = new DataTable();
dt = eobj.GetTicketUpdates(txtSupportRef.Text);
int i = 0;
if (i < dt.Rows[0].Table.Rows.Count - 1)
{
i++;
txtShortDesc.Text = dt.Rows[0].Table.Rows[i]["ShortDesc"].ToString();
txtNextStep.Text = dt.Rows[0].Table.Rows[i]["NextStep"].ToString();
txtLastUpdated.Text = dt.Rows[0].Table.Rows[i]["LastUpdated"].ToString();
}
else
{
//no records to see more.
}
}
Also the user will have the ability to load different ticket references into the form. So i need the ability to quickly iterate based on the loaded ticket.
If you need more info please ask.
Your solution will work if you properly manage the index wrap-around.
You just need to reset the index to 0 when it hits the end of the data set, and set it to DataTable.Rows.Count - 1 when it goes back from the beginning.
INITIALIZE
DataTable dt;
protected override void Page_Load
{
// querying each load gets expensive; consider alternative patterns?
dt = GetData(txtSupportRef.Text);
if (!IsPostBack)
{
int i = 0;
ViewState["recordIndex"] = i;
PopulateForm(i);
}
}
CREATE REUSABLE FUNCTIONS - THEY SAVE YOU MILLION$
protected DataTable GetData(string supportRef)
{
DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter eobj = new DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter();
return eobj.GetTicketUpdates(supportRef);
}
protected void PopulateForm(int i)
{
ViewState["recordIndex"] = i
System.Data.DataRow row = dt.Rows[0].Table.Rows[i];
txtShortDesc.Text = row["ShortDesc"].ToString();
txtNextStep.Text = row["NextStep"].ToString();
txtLastUpdated.Text = row["LastUpdated"].ToString();
}
PREVIOUS BUTTON
protected void btnPrevious_Click1(object sender, EventArgs e)
{
int i = (int)ViewState["recordIndex"];
i = i <= 0 ? dt.Rows[0].Table.Rows.Count - 1 : i-1;
PopulateForm(i);
}
NEXT BUTTON
protected void btnNext_Click1(object sender, EventArgs e)
{
int i = (int)ViewState["recordIndex"];
i = i >= dt.Rows[0].Table.Rows.Count - 1 ? 0 : i+1;
PopulateForm(i);
}
Here is a JavaScript example that illustrates the algorithm and shows how much more responsive it is when doing this client side instead. It is applicable to C# also
var startWith = 1; // or 0, etc.
var pageNo = startWith;
var recordCount = 5;
var records = [];
for(var i = 0; i < recordCount; i++)
{
records.push({TicketRef: "000-00-"+ i, ShortDescription: ['BLUE SCREEN','VIRUS INFECTION','NEW HARDWARE','SOFTWARE UPGRADE','SPILLED SODA ON KEYBOARD','BROKEN CD-ROM'][Math.random()*6|0], NextStep: ['ESCALATE','RESOLVE','RETURN','CLOSE','INVESTIGATE','TRANSFER'][Math.random()*6|0], LastUpdated: ((Math.random()*12|0)+1).toString() + " hours ago" });
}
renderPageNo = function() {
$("h1").text(pageNo);
$("input:text").each(function(){this.value=records[pageNo-startWith][$(this).data("field")];});
}
prev = function() {
if (pageNo > startWith) pageNo--
else pageNo = recordCount;
renderPageNo(pageNo);
}
next = function() {
if (pageNo < recordCount) pageNo++
else pageNo = startWith;
renderPageNo(pageNo);
}
renderPageNo();
$(".btn").eq(0).on("click", prev).end().eq(1).on("click", next);
*
{
font-family: 'Segoe UI';
}
label
{
width: 200px;
display:block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1></h1>
<label>Ticket Ref<input type='text' data-field="TicketRef"/></label>
<label>Short Description<input type='text' data-field="ShortDescription"/></label>
<label>Next Step<input type='text' data-field="NextStep"/></label>
<label>LastUpdated<input type='text' data-field="LastUpdated"/></label>
<input class='btn' type='button' value='PREV' />
<input class='btn' type='button' value='NEXT' />
I would provide an index in your database table, which you can then load into the datatable together with the rest of the data. Then you can use that index to go forward and backwards with no hustle by extracting row details based on the index:
// store your current row index in a ViewState when first loading the data
ViewState["currentIndex"] = (int)row["RowIndex"];
//When going forward increment your rowIndex to find out the new Row Index
int currentRowIndex = (int) ViewState["currentIndex"];
currentRowIndex++; // ++ to go next -- to go previous
//Get the current row based on rowindex
DataRow row = myDataTable.Select("ID=" + currentRowIndex);
// then use the row data and assign to your textboxes
So for example a unique identity key, which is auto incremented by 1 in your database table will help to solve the problem.
Let me break it down a bit further:
Lets assume you fetch your database data on the page Load event:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) {
// Code to Fetch database data and assign it to a datatable
// Save DataTable in a ViewState["currentTable"]
// Get Last Row and assign field values to textboxes
// Get Last Row index value and assign it to ViewState["currentRowIndex"]
}
}
protected void cmdMoveNext_Click(){
// in this function you going to get your current row index from the ViewState[currentRowIndex]
int currentIndex = (int)ViewState["currentRowIndex"];
currentIndex++; // set next row index (currentIndex=currentIndex+1)
// declare a datatable and assing viewstate["currentTable"] to it
DataTable myTable = (DataTable)ViewState["currentTable"];
// Find the DataRow at the new index
DataRow row = (DataRow) myTable.Select("search by index code");
// Use the new row values to assign to textboxes
// Save the new row index in ViewState
}
I have done the same thing but much simpler and without using additional indexers.
Here is what I have done:
public OnLoad(...)
{
if(!Page.IsPostBack)
{
BuidNavigationList();
}
LoadNavigation();
}
private List<string> NavigationList
{
get
{
if (ViewState["NavigationList"] != null)
{
return (List<string>)ViewState["NavigationList"];
}
return null;
}
set
{
ViewState["NavigationList"] = value;
}
}
private void BuidNavigationList()
{
// ds get
if (ds.Tables.Count == 1)
{
DataTable dataTable = ds.Tables[0];
if (dataTable.Columns.Contains("YourSwitchColumn"))
{
NavigationList = new List<string>(dataTable.Rows.Cast<DataRow>().Select(r => r["YourSwitchColumn"]).Cast<String>());
}
}
}
private string YourCurrentSwitchColumnValue
{
get
{
return Request["YourSwitchColumn"].ToString();
}
}
private void LoadNavigation()
{
if(this.NavigationList == null)
{
lblPrevious.Enabled = false;
lblNext.Enabled = false;
}
int navigationListIndex = NavigationList.IndexOf(YourCurrentSwitchColumnValue);
if (navigationListIndex == 0)
{
lblPrevious.Enabled = false;
lblNext.Enabled = true;
lblNext.Value = NavigationList[navigationListIndex + 1];
}
else if (navigationListIndex > 0 && navigationListIndex < NavigationList.Count - 1)
{
lblPrevious.Enabled = true;
lblNext.Enabled = true;
lblPrevious.Value = NavigationList[NavigationListIndex - 1];
lblNext.Value = NavigationList[NavigationListIndex + 1];
}
else if (navigationListIndex == NavigationList.Count - 1)
{
lblPrevious.Enabled = true;
lblNext.Enabled = false;
lblPrevious.Value = NavigationList[NavigationListIndex - 1];
}
}
public void lblNext_OnClick(object sender, ...)
{
Request.Redirect("SamePage.aspx?YourSwitchColumn=" + valueFrom_lblNext);
}
Note this is a demo code you should implement on your needs
In my case my grid view can contain normal text or a hyperlink . I want to get the value of those fields. So far I have tried
DataTable detailTable = new DataTable();
for (int i = 0; i < gvTransactionDetails.Columns.Count; i++)
{
detailTable.Columns.Add(gvTransactionDetails.HeaderRow.Cells[i].Text.ToString());
}
foreach (GridViewRow gvrow in gvTransactionDetails.Rows)
{
DataRow dr = detailTable.NewRow();
for (int j = 0; j < gvTransactionDetails.Columns.Count; j++)
{
Control hyperLink = gvrow.Cells[j].Controls[0] as LiteralControl;
if (hyperLink != null)
{
dr[j] = ((LiteralControl)gvrow.Cells[j].Controls[0]).Text.ToString();
}
else
{
dr[j] = gvrow.Cells[j].Text.ToString();
}
}
detailTable.Rows.Add(dr);
}
The problem I am facing is as the first cell in each and every row is a hyperlink and rest all other cells only contain text values, after the first iteration of the foreach loop only I am getting "Specified argument was out of the range of valid values.
Parameter name: index" exception.
Any idea how to fix it ?
You can do this instead in RowDataBound event of gridview like this:-
protected void gvTransactionDetails_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton lnkId= (LinkButton)e.Row.FindControl("lnkId");
if (lnkId!= null)
{
detailTable.Rows.Add(lnkId.Text);
}
}
}
You can attach this event like this to your gridview:-
<asp:GridView ID="gvTransactionDetails" runat="server"
OnRowDataBound="gvTransactionDetails_RowDataBound">
Also, as a side note from your posted code, .Text.ToString(); the Text property anyways returns a String so no need to convert it using ToString.
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();