How to get the cell value by column name not by index in GridView in asp.net - asp.net

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)

Related

How to make dynamic column header clickable in gridview

I have a gridview with many columns. It's sortable, Allow Sorting="True", each column has Sort Expression. For each column sorting works just fine, except for 10 columns that have dynamic headers that I assign in Row_Databound event:
protected void gvSearchResults_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
for (int i = 1; i < 11; i++)
{
if (Session["Label" + i.ToString()] !=null)
{
e.Row.Cells[i].Text = Session["Label" + i.ToString()].ToString();
}
}
}
}
These 10 columns are not clickable. Is there any way to make them clickable? Everything else in these columns is enabled for sorting.
I've got some suggestions from a different forum about creating columns in Page_Load or Page_Init events, but this probably won't work for me.
Thank you.
You can replace the text of the existing LinkButton in the header cell:
protected void gvSearchResults_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
for (int i = 1; i < 11; i++)
{
string caption = Session["Label" + i.ToString()] as string;
if (caption != null)
{
TableCell headerCell = e.Row.Cells[i];
LinkButton lnkSort = headerCell.Controls[0] as LinkButton;
lnkSort.Text = caption;
}
}
}
}
It can be done. If you look at the HTML code you will see something similar to this as the link for sorting the GridView.
yourColumnName
We need to recreate that link in the RowDataBound function.
for (int i = 1; i < 11; i++)
{
//first we cast the sender as a gridview
GridView gv = sender as GridView;
//get the unique ID of the gridview, this is different from ClientID which you normally would use for JavaScipt etc
string uniqueID = gv.UniqueID;
//then get the SortExpression for the column
string sortExpression = gv.Columns[i].SortExpression;
//get the new column name from the session
string yourColumnName = string.Empty;
if (Session["Label" + i.ToString()] != null)
{
yourColumnName = Session["Label" + i.ToString()].ToString();
}
//and then we fill the header with the new link
e.Row.Cells[i].Text = "" + yourColumnName + "";
}
However for this to work, enableEventValidation has to be set to false, which is NOT recommended. Otherwise you will get the "Invalid postback or callback argument" error.
Better would be changing the column names somehow before the data is bound to the gridview.
Thank you very much for your help. The solution with LinkButton worked great for me:
protected void gvSearchResults_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
for (int i = 1; i < 11; i++)
{
if (Session["Label" + i.ToString()] !=null)
{
((LinkButton)(e.Row.Cells[i].Controls[0])).Text = Session["Label" + i.ToString()].ToString();
}
}
}
}

Change values of a single column of a databound gridview and display it in the same gridview

Its a weird requirement but, Can I alter values of a single column of a databound gridview and display it in the same gridview ? Say, in GridView_RowDataBound(object sender, GridViewRowEventArgs e). I have edited the code for what I am actually doing. Problem is "DataItem" is a typeof class(entity fetched db table) and converting it to Datarow is not possible. So how do i go about it?
protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
DataRowView drv = (DataRowView)e.Row.DataItem;
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (drv["Id"] != DBNull.Value)
{
string val = CommonUtil.Decrypt(drv["Id"].ToString());
e.Row.Cells[3].Text = val ;
}
}
}
Yes it's doable in GridView RowDataBound something like:
if(e.Row.RowType==DataControlRowType.DataRow)
{
//say you want to set value of 3rd column to "Hello"
// e.Row.Cells[2].Text="Hello"; //0 based index
MyCustomClass myitem= (MyCustomClass) e.Row.DataItem;
if (myitem.Id != null)
{
string val = CommonUtil.Decrypt(myitem.Id.ToString());
e.Row.Cells[3].Text = val ;
}
}
If that doesn't help, please share some code and more information on exactly what column you want to change and to what value.
You can cast to the type you want:
MyCustomClass drv = (MyCustomClass) e.Row.DataItem;

How to access a gridview column on rowdatabound ?

I would like to change the value of my gridview column to active when the value is 1.
I have gridview column like
<asp:BoundField DataField="STATUS" HeaderText="STATUS" SortExpression="STATUS" HeaderStyle-HorizontalAlign="Left">
<HeaderStyle HorizontalAlign="Left"></HeaderStyle>
</asp:BoundField>
and cs code
protected void gvCategory_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.Cells[5].Text=="0")
{
e.Row.Cells[5].Text = "INACTIVE";
}
}
}
This is working but it would fail if i change the column order.
What i need is something like findControl function.
Thanks.
Here's a few utilities I wrote years ago that may help you:
// ---- GetCellByName ----------------------------------
//
// pass in a GridViewRow and a database column name
// returns a DataControlFieldCell or null
static public DataControlFieldCell GetCellByName(GridViewRow Row, String CellName)
{
foreach (DataControlFieldCell Cell in Row.Cells)
{
if (Cell.ContainingField.ToString() == CellName)
return Cell;
}
return null;
}
// ---- GetColumnIndexByHeaderText ----------------------------------
//
// pass in a GridView and a Column's Header Text
// returns index of the column if found
// returns -1 if not found
static public int GetColumnIndexByHeaderText(GridView aGridView, String ColumnText)
{
TableCell Cell;
for (int Index = 0; Index < aGridView.HeaderRow.Cells.Count; Index++)
{
Cell = aGridView.HeaderRow.Cells[Index];
if (Cell.Text.ToString() == ColumnText)
return Index;
}
return -1;
}
// ---- GetColumnIndexByDBName ----------------------------------
//
// pass in a GridView and a database field name
// returns index of the bound column if found
// returns -1 if not found
static public int GetColumnIndexByDBName(GridView aGridView, String ColumnText)
{
System.Web.UI.WebControls.BoundField DataColumn;
for (int Index = 0; Index < aGridView.Columns.Count; Index++)
{
DataColumn = aGridView.Columns[Index] as System.Web.UI.WebControls.BoundField;
if (DataColumn != null)
{
if (DataColumn.DataField == ColumnText)
return Index;
}
}
return -1;
}
You could loop all columns to get the correct index or use this LINQ:
String colToFind = "status";
int colIndex = ((GridView)sender).Columns.Cast<DataControlField>()
.Where((c, index) => c.HeaderText.ToLower().Equals(colToFind))
.Select((c,index)=>index).First();
Cleaner easier to read LINQ. Tim's did not work for me.
private int GetFirstGridViewColIndex(string dataField, object sender)
{
var boundFieldColumns = ((GridView)sender).Columns.Cast<BoundField>();
return boundFieldColumns.Where((column, index) => string.Equals(column.DataField, dataField, StringComparison.InvariantCultureIgnoreCase))
.Select((column, index) => index)
.First();
}

how to update a data table row value to hyperlink?

I have a data table in dt in c# code and it has column[0] datatype is int.So when ever I reach 7th value in the table I need to convert to hyperlink and add it back to data table.
int x = int.Parse(dt.Rows[7][0].ToString());
dt.Row[7][0] = "" + x + "";
but it is givin me the error that can not accept string value to integer. How to over come this?
Correct me I'm wrong. Add an extra column of string type.
dt.Columns.Add("LinkColumn");
...
dt.Rows[7]["LinkColumn"]=string.Format("<a href='#'>{0}</a>",x);
How are you creating the data table? The column has probably been typed to int, so it can not accept string values.
A better way might be to change the data bound control to show the link
protected override void OnInit(EventArgs e) {
base.OnInit(e);
DataBound += new EventHandler(GridView1_DataBound);
}
void GridView_RowDataBound(object sender, GridViewRowEventArgs e) {
GridView gridview = (GridView)sender;
if (e.Row.RowType == DataControlRowType.DataRow) {
for (int i = 0; i < gridview.Columns.Count; i++) {
DataControlField obj1 = gridview.Columns[i];
if (obj1.GetType() == typeof(BoundField)) {
BoundField field = (BoundField)obj1;
string datafield = field.DataField;
object value = DataBinder.Eval(e.Row.DataItem, datafield);
Literal c = new Literal();
c.Text = "";
e.Row.Cells[i].Controls.Add(c);
}
}
}
}

How can I change the field type on a GridView at runtime with AutoGenerate="True"?

I've created a control that extends the BoundField control to do some special processing on the data that's passed into it.
I now have a grid that has AutoGenerateColumns="true", by which I'd like to intercept the HeaderText, see if it's a particular value and then swap in the "SpecialBoundField" instead. I've tried using the OnDataBinding event to loop through the columns, but at this point there are no columns in the grid. I think that RowDataBound and DataBound are too late in the game so not sure what to do.
My next thought was to override the grid control itself to add in a "AutoGeneratingColumn" event in
protected virtual AutoGeneratedField CreateAutoGeneratedColumn(AutoGeneratedFieldProperties fieldProperties)
Can anyone help or point me in a better direction? Thanks!
If you have both fields coming back in the dataset, I would suggest setting the column visibilities instead of trying to dynamically add or change the datafields. Invisible columns don't render any HTML, so it would just be a matter of looking at the header row when it gets bound, checking the field you're interested in, and setting the column visibility.
void myGridView_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
if (e.Row.Cells[1].Text = "BadText")
{
myGridView.Columns[1].Visible = false;
myGridView.Columns[5].Visible = true;
}
}
}
What I ended up with:
public class SpecialGridView : GridView
{
protected override void OnRowDataBound(GridViewRowEventArgs e)
{
ModifyData(e);
base.OnRowDataBound(e);
}
IList<string> _columnNames = new List<string>();
protected void ModifyData(GridViewRowEventArgs e)
{
LoadColumnNames(e);
if (e.Row.RowType == DataControlRowType.DataRow)
{
for (int i = 0; i < e.Row.Cells.Count; i++)
{
string currentColumnName = _columnNames[i];
if (IsSpecialColumn(currentColumnName))
{
string text = e.Row.Cells[0].Text;
bool isSpecialData = text.ToUpper() == "Y";
if (isSpecialData)
{
e.Row.Cells[i].CssClass += " specialData";
}
}
}
}
}
private void LoadColumnNames(GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
foreach (TableCell cell in e.Row.Cells)
{
_columnNames.Add(cell.Text);
}
}
}
private bool IsSpecialColumn(string currentColumnName)
{
foreach (string columnName in SpecialColumnNames)
{
if (currentColumnName.ToUpper() == columnName.ToUpper())
{
return true;
}
}
return false;
}
private IList<string> _specialColumnNames = new List<string>();
public IList<string> SpecialColumnNames
{
get { return _specialColumnNames; }
set { _specialColumnNames = value; }
}
}

Resources