I have a GRID that consist of columns that are populated and recently I have added some drop down columns as well to the grid. My drop down is populating with the options from the backend, but now I need to save those answers to the page itself, so the user can see it on the screen next time they arrive on the page.
GRID
protected void gvViolationsCited_OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow || e.Row.RowType == DataControlRowType.Header)
{
TableCell idCell = GridViewFunctions.FindCellByDataField(e.Row, "ID");
idCell.Visible = false;
if (e.Row.RowType == DataControlRowType.DataRow)
{
ImageButton lb = (ImageButton)e.Row.FindControl("lbShowModalDialog");
long id = long.Parse(idCell.Text);
DropDownList hpvcriteria = (DropDownList)e.Row.FindControl("hpvcriteria");
hpvcriteria.DataSource = LookupDataLoaderService.Instance.LoadLookupData(LookupTables.HPV_VIOLATION_CODE);
hpvcriteria.DataBind();
}
}
}
private void PopulateViolationGrid(long caseID)
{
IList<EnforcementViolation> enforcementViolations = LovService.Instance.GetEnforcementLovList(caseID);
IEnumerator<EnforcementViolation> evEnumerator = enforcementViolations.GetEnumerator();
while (evEnumerator.MoveNext())
{
EnforcementViolation ev = evEnumerator.Current;
//txtOtherDocsViolated.Text += ev.Permit + " ";
}
gvViolationsCited.DataSource = GridViewFunctions.GenericListToDataTable<EnforcementViolation>(enforcementViolations);
gvViolationsCited.DataBind();
upnlViolationsCited.Update();
evEnumerator.Dispose();
}
I have a gridview placed on a ASP.Net WebSite, and added a column dynamically like this:
protected void gvGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (DataControlRowType.DataRow == e.Row.RowType && e.Row.RowState != DataControlRowState.Edit &&
(e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate))
{
TextBox tb = new TextBox();
tb.ID = "tb";
tb.Attributes.Add("runat", "server");
int i = e.Row.Cells.Count;
i = i - 1;
e.Row.Cells[i].Controls.Add(tb);
}
}
The gridview is populated, and at the end of every row the TextBox is added. When I look at the HTML Code that is delived to the Browser, I can see that the ID of every Textbox is "gv_Items_tb_X" with X being the current row index.
Now I would like to access the content that the user types in the tb on a button click. I tried following code, but it I get an exception becuase the textbox is null.
protected void btn_UpdateCount_Click(object sender, EventArgs e)
{
OI_Data_Service.OI_Data_ServiceClient sc = new OI_Data_Service.OI_Data_ServiceClient();
foreach (GridViewRow row in gv_Items.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
TextBox tb_value = (TextBox)gv_Items.Rows[row.RowIndex].Cells[5].FindControl("gv_Items_tb_" + row.RowIndex);
sc.UpdateItemOnOpening("1", row.Cells[0].Text, tb_value.Text);
}
}
Response.Redirect("~/okay.aspx");
}
Can anyone tell my what I am doing wrong?
Thanks!
The .NET DataGrid control has an AutoGeneratingColumn Event that fires once for every bound data item right after the data source is changed. This is useful because you could define certain columns in the template, like so:
<Columns>
<asp:HyperLinkField DataNavigateUrlFields="ID" DataNavigateUrlFormatString="ww{0}" DataTextField="ID" DataTextFormatString="{0}" HeaderText="ID" />
</Columns>
and then prevent the same column from being replicated when columns are autogenerated from your data source. In this example, you could prevent the ID column from being autogenerated like this:
Private Sub DG_AutoGeneratingColumn(ByVal sender As Object, ByVal e As DataGridAutoGeneratingColumnEventArgs)
Dim headername As String = e.Column.Header.ToString()
If headername = "ID" Then
e.Cancel = True
End If
End Sub
My question is whether a similar functionality can be achieved with a GridView control.
DETAILS
The data source for the gridview is a DataTable object, which I am binding like so:
GridView1.DataSource = results.Tables("Vitals")
GridView1.DataBind()
The number of columns in my DataTable will vary, which is why it is extremely convenient for me to use AutoGenerateColumns.
To do that you should handle the RowCreated event and write something like the following:
private List<int> hideColumnsIndexes = new List<int>();
protected void Page_Load(object sender, EventArgs e)
{
hideColumnsIndexes.Clear();
}
protected void GridView1_OnRowCreated(object sender, GridViewRowEventArgs e)
{
//Find indexes
if (e.Row.RowType == DataControlRowType.Header)
{
for (int i = 0; i < e.Row.Cells.Count; i++ )
{
if (e.Row.Cells[i].Text == "Id")
{
hideColumnsIndexes.Add(i);
}
//Add more columns to hide
}
}
//Hide cells
foreach (var index in hideColumnsIndexes)
{
e.Row.Cells[index].Visible = false;
}
}
I am having a gridview in asp.net and now I want the cell value by the column name but not by the cell index.
How would be it possible by retrieving the cell value by the cell column name
GridView does not act as column names, as that's it's datasource property to know those things.
If you still need to know the index given a column name, then you can create a helper method to do this as the gridview Header normally contains this information.
int GetColumnIndexByName(GridViewRow row, string columnName)
{
int columnIndex = 0;
foreach (DataControlFieldCell cell in row.Cells)
{
if (cell.ContainingField is BoundField)
if (((BoundField)cell.ContainingField).DataField.Equals(columnName))
break;
columnIndex++; // keep adding 1 while we don't have the correct name
}
return columnIndex;
}
remember that the code above will use a BoundField... then use it like:
protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
int index = GetColumnIndexByName(e.Row, "myDataField");
string columnValue = e.Row.Cells[index].Text;
}
}
I would strongly suggest that you use the TemplateField to have your own controls, then it's easier to grab those controls like:
<asp:GridView ID="gv" runat="server">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Label ID="lblName" runat="server" Text='<%# Eval("Name") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
and then use
string columnValue = ((Label)e.Row.FindControl("lblName")).Text;
Although its a long time but this relatively small piece of code seems easy to read and get:
protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
int index;
string cellContent;
foreach (TableCell tc in ((GridView)sender).HeaderRow.Cells)
{
if( tc.Text.Equals("yourColumnName") )
{
index = ((GridView)sender).HeaderRow.Cells.GetCellIndex(tc);
cellContent = ((GridView)sender).SelectedRow.Cells[index].Text;
break;
}
}
}
You can use the DataRowView to get the column index.
void OnRequestsGridRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var data = e.Row.DataItem as DataRowView;
// replace request name with a link
if (data.DataView.Table.Columns["Request Name"] != null)
{
// get the request name
string title = data["Request Name"].ToString();
// get the column index
int idx = data.Row.Table.Columns["Request Name"].Ordinal;
// ...
e.Row.Cells[idx].Controls.Clear();
e.Row.Cells[idx].Controls.Add(link);
}
}
}
For Lambda lovers
protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var boundFields = e.Row.Cells.Cast<DataControlFieldCell>()
.Select(cell => cell.ContainingField).Cast<BoundField>().ToList();
int idx = boundFields.IndexOf(
boundFields.FirstOrDefault(f => f.DataField == "ColName"));
e.Row.Cells[idx].Text = modification;
}
}
Based on something found on Code Project
Once the data table is declared based on the grid's data source, lookup the column index by column name from the columns collection. At this point, use the index as needed to obtain information from or to format the cell.
protected void gridMyGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataTable dt = (DataTable)((GridView)sender).DataSource;
int colIndex = dt.Columns["MyColumnName"].Ordinal;
e.Row.Cells[colIndex].BackColor = Color.FromName("#ffeb9c");
}
}
Header Row cells sometimes will not work. This will just return the column Index. It will help in a lot of different ways. I know this is not the answer he is requesting. But this will help for a lot people.
public static int GetColumnIndexByHeaderText(GridView gridView, string columnName)
{
for (int i = 0; i < gridView.Columns.Count ; i++)
{
if (gridView.Columns[i].HeaderText.ToUpper() == columnName.ToUpper() )
{
return i;
}
}
return -1;
}
A little bug with indexcolumn in alexander's answer:
We need to take care of "not found" column:
int GetColumnIndexByName(GridViewRow row, string columnName)
{
int columnIndex = 0;
int foundIndex=-1;
foreach (DataControlFieldCell cell in row.Cells)
{
if (cell.ContainingField is BoundField)
{
if (((BoundField)cell.ContainingField).DataField.Equals(columnName))
{
foundIndex=columnIndex;
break;
}
}
columnIndex++; // keep adding 1 while we don't have the correct name
}
return foundIndex;
}
and
protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
int index = GetColumnIndexByName(e.Row, "myDataField");
if( index>0)
{
string columnValue = e.Row.Cells[index].Text;
}
}
}
We can get it done in one line of code. No need to loop through anything or call other methods.
protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string cellValue = e.Row.Cells[e.Row.Cells.GetCellIndex(e.Row.Cells.Cast<DataControlFieldCell>().FirstOrDefault(cell => cell.ContainingField.HeaderText == "columnName"))].Text;
}
}
//get the value of a gridview
public string getUpdatingGridviewValue(GridView gridviewEntry, string fieldEntry)
{//start getGridviewValue
//scan gridview for cell value
string result = Convert.ToString(functionsOther.getCurrentTime());
for(int i = 0; i < gridviewEntry.HeaderRow.Cells.Count; i++)
{//start i for
if(gridviewEntry.HeaderRow.Cells[i].Text == fieldEntry)
{//start check field match
result = gridviewEntry.Rows[rowUpdateIndex].Cells[i].Text;
break;
}//end check field match
}//end i for
//return
return result;
}//end getGridviewValue
It is possible to use the data field name, if not the title so easily, which solved the problem for me. For ASP.NET & VB:
e.g. For a string:
Dim Encoding = e.Row.DataItem("Encoding").ToString().Trim()
e.g. For an integer:
Dim MsgParts = Convert.ToInt32(e.Row.DataItem("CalculatedMessageParts").ToString())
protected void CheckedRecords(object sender, EventArgs e)
{
string email = string.Empty;
foreach (GridViewRow gridrows in GridView1.Rows)
{
CheckBox chkbox = (CheckBox)gridrows.FindControl("ChkRecords");
if (chkbox != null & chkbox.Checked)
{
int columnIndex = 0;
foreach (DataControlFieldCell cell in gridrows.Cells)
{
if (cell.ContainingField is BoundField)
if (((BoundField)cell.ContainingField).DataField.Equals("UserEmail"))
break;
columnIndex++;
}
email += gridrows.Cells[columnIndex].Text + ',';
}
}
Label1.Text = "email:" + email;
}
protected void gvResults_PreRender(object sender, EventArgs e)
{
var gridView = (GridView)sender;
gridView.GetColumnByName("YourDataBoundDataField").Visible = true;
}
Extension:
public static DataControlField GetColumnByName(this GridView gridView, string columnName)
{
int columnIndex = -1;
for (int i = 0; i < gridView.Columns.Count; i++)
{
if (gridView.Columns[i].HeaderText.Trim().Equals(columnName, StringComparison.OrdinalIgnoreCase))
{
columnIndex = i;
break;
}
}
if (columnIndex == -1)
{
throw new ArgumentOutOfRangeException("GridViewRow does not have the column with name: " + columnName);
}
return gridView.Columns[columnIndex];
}
The primary reason this would be difficult is because gridview cells do not have accessible cell names (ugh).
In order to work around this handicap you can make an extension method (mine is in VB.NET, but #Дмитрийh seems to have a similar solution in C#).
To work around this ensure the HeaderText of the gridview cells have the same value as the cellnames and grab the values via that HeaderText name.
Here is extension methods you would need for strings and integers in a VB.NET code snippet that I made
Public Shared Function GetStringByCellName(pGridViewRow As GridViewRow, pCellName As String) As String
For Each myCell As DataControlFieldCell In pGridViewRow.Cells
If myCell.ContainingField.ToString() = pCellName Then
Return myCell.Text
End If
Next
Return Nothing
End Function
And the difference for integers being a parse/cast
Public Shared Function GetIntegerByCellName(pGridViewRow As GridViewRow, pCellName As String) As Integer
For Each myCell As DataControlFieldCell In pGridViewRow.Cells
If myCell.ContainingField.ToString() = pCellName Then
Return Integer.Parse(myCell.Text)
End If
Next
Return Nothing
End Function
And calling the functions would look like this if it were in a class.
Dim columnNamesStringValue As String = ExtensionMethodsClassName.GetStringByCellName(pGridViewRow, "stringOfColumnName")
Dim columnNamesIntegerValue As Integer = ExtensionMethodsClassName.GetIntegerByCellName(pGridViewRow, "stringOfColumnName")
have in mind that you may not always get a value, and instead may get Nothing
Before you (perhaps try to put the value in your database), ensure it is not nothing first by checking that it is not nothing. If you wanted to insert into the database, however, it may be better to return a System.DBNull instead of Nothing from the extension methods I provided.
(DO NOT CHECK AND INSERT THE OTHER NULLS or Nothing AS DBNull'S. DIFFERENT TYPES OF NULL'S AND NOTHINGS ARE NOT EQUAL)
If (columnNamesStringValue IsNot Nothing)
In web applicatiom i am trying to find grid controls in RowDataBound event. But it is giving object reference to instance of an object, this is my code :
protected void mygrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
string empid = "";
empid = ((Label)e.Row .FindControl("lblname")).Text;
}
Can you hlep me please to find the control, thank you.
Ya, i got the answer, i have to place
string empid = "";
if (e.Row.RowType == DataControlRowType.DataRow)
{
empid = ((Label)e.Row.FindControl("lblname")).Text;
}
then i we get the control
Find control for Data rows only Like:
protected void mygrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string empid = "";
empid = ((Label)e.Row .FindControl("lblname")).Text;
}
}
The "Object reference to instance of an object" error is probably because no control named lblname was found for the current row.
Maybe you need to check the type of the row, e.Row.RowType = DataControlRowType.DataRow so that you are not searching for the control in the header row.
Label lbl = (Label)e.Row.Cells[2].FindControl("lblCreatedBy");
lbl.Text = "ABC";