Is there a way to access elements that are in another row, while you're in a RowDataBound event?
public void gridview1_RowDataBound(object sender, GridViewRowEventArgs e)
{
// how to compare e.Row with row above or row below?
}
One thing I think you can do is...
foreach (GridViewRow gvrow in GridView1)
{
//loop through gridview's rows and find the row you're looking for
}
I believe that RowDataBound adds the rows in the same order that the data source has them in, therefore if there are 100 records in your DataTable and you bind that DataTable to the GridView, RowDataBound gets called 100 times, for each row, in the order they exist in the DataTable. Therefore, you mentioned using RowDataBound to compare e.Row with the row above OR row below...but if they are coming in sequentially, there is no row below. This is something I hadn't really thought about before but I recall when doing testing with breakpoints that RowDataBound functions in this manner.
If you can't get something like that working, and you need this to happen even if it isn't pretty and don't get any better answers, you can store a copy of your databound DataTable or whatever your source for data is into a ViewState or Session variable, like ViewState["myDataTable"]. You can then retrieve the DataTable on RowDataBound event, and look at the rows above and below the row that is represented by e.Row (provided that you have some kind of cursor like an ID to identify the rows). By doing this, you CAN look at the next row that will be added to the GridView by RowDataBound, because it will be the next row in the DataTable (which already exists and can be viewed).
If you're doing stuff on a massive scale though, I imagine all this looping could get cumbersome.
From a foggy memory, you can reference GridView's array of Rows from there and iterate them. You should be able to get e's position and access in any case the rows above, not sure about rows below as they aren't databound by then yet I suppose, unless this is a one-row rebind shot.
Related
I have a databound DataGridView in a Win Forms app which the user may have sorted by a column. The problem is this: after the user leaves a row after editing a cell in the sorted column, the row is immediately re-sorted.
This is very disorienting for users and makes editing groups of rows together impossible.
The solution I'm looking for will effectively disable automatic re-sorting after an initial sort and then only sort again when the user requests it.
For the benefit of others, here is the solution I came up with, but I'd love to hear a better one.
I added an additional, non-persistent column to the DataTable called SORT_ORDER which is used only for sorting.
When the user clicks a column to sort, I copy the values and value type from the selected column to the SORT_ORDER column and then sort on SORT_ORDER. Since the SORT_ORDER is not visible and can't be edited, the sort order does not change even if the user edits the selected column. The event handler looks like this:
private void MyDataGridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) {
dirtyCellListenerEnabled = false;
SORT_ORDER.ValueType = MyDataGridView.Columns[e.ColumnIndex].ValueType;
foreach(DataGridViewRow r in MyDataGridView.Rows) {
r.Cells[SORT_ORDER.Index].Value = r.Cells[e.ColumnIndex].Value;
}
switch(MyDataGridView.SortOrder) {
case System.Windows.Forms.SortOrder.None:
MyDataGridView.Sort(SORT_ORDER, ListSortDirection.Ascending);
break;
case System.Windows.Forms.SortOrder.Ascending:
MyDataGridView.Sort(SORT_ORDER, ListSortDirection.Descending);
break;
case System.Windows.Forms.SortOrder.Descending:
MyDataGridView.Sort(SORT_ORDER, ListSortDirection.Ascending);
break;
}
dirtyCellListenerEnabled = true;
}
Note that I had to disable and re-enable my cell listener so that my code doesn't treat the sort column update as a real change.
Before arriving at this solution, I had also tried adding the sort column to the DataGridView, but it doesn't work because the DataGridView can't sort on a column that doesn't exist in its data source.
I'm sure there are some other tweaks I could do, too, like suspending updates while populating the SORT_ORDER and displaying the sort glyph on the selected column.
This is a real pain as i'm finding out right now. Grids are brutally complicated sometimes for seemingly nothing
for each selected cell, I store the primary key, and the name of the grid column (i made a tiny class to hold those).
Then I throw them all into a list and iterate through them for the updating. Each time I update a cell value, i search for where the actual cell is now and replace my local reference variable to that cell so I can keep going in the code.
Cell.Value = ValueToWrite
Cell = FindCell(Cell.OwningRow.DataGridView, DataRow, ColName)
Function FindCell(Grid As DataGridView, DataRow As DataRow, ColName As String) As DataGridViewCell
'Find the same cell, wherever you may be now, damn you sort.
Dim GridRow = (From x As DataGridViewRow In Grid.Rows Where x.DataBoundItem.row Is DataRow).FirstOrDefault
Dim Cell = GridRow.Cells(ColName)
Return Cell
End Function
I've encountered this problem and couldn't get a decent answer, so I tried this and it worked,
private void SortBoundDG()
{
DataTable TempTable;
TempTable = (DataTable)DG.DataSource;
TempTable.DefaultView.Sort = ColumnName + " " + "DESC";
DG.DataSource = TempTable.DefaultView.ToTable();
}
simply convert the defaultview back to a table and set it as a source to your datagridview
Sounds like your GridView is data binding all over again. This means that your sort order will be lost. Enable the Viewstate of your gridview and make sure that you aren't binding the grid on postback.
I'd like to get a grid with ability to add subheader (one or several rows) according to value of certain column (data source is grouped sorted by this column). Within my inherited GridView I override OnRowDataBind method: If value of considering column on current call of OnRowDataBind is not equal to previous values, then special header row is inserted before current row.
Table tbl = this.Controls[0] as Table; //this - pointer to CustomGridView
tbl.Controls.AddAt(rowIndex + add_counter + 1, NewSubHeaderRow); /*rowIndex - RowIndex of current row, add_counter - amount of already added SubHrader rows */
All works right. But problem occures on postback, when GridView restores it's state. First row and all its controls into every group (other words, first row after each dynamically added subheader row) comes without any attributes. But second row in group keeps both its own attributes and attributes of first row.
Some help will be very usefull. Maybe there is another (and right) way to add row into GridView. Then please, provide me with links to tutorials or articles.
Dynamic controls disappear on postback because on postback framework doesn't have information about such controls. It's programmer's responsibility to keep track of these dynamic controls and recreate them after postback...
Please refer to following post for re-creating dynamic controls on postback:
FAQ: Why do dynamic controls disappear on postback
Thanks for Waqas.
Here is my decision.
I override CreateChild(datasource, isBinding) and create list where put index of row, before which you should add subheader row. On SaveViewState i add this list to sealized object. On Load ViewState i load this list and if it's not null create subheader rows by saved indexes.
Maybe, there is a simpler and more natural way. But it's hidden from me securely )))
I need to get all the rows in a DataGridView in a foreach function. How can I do this?
I.E. foreach() for each row, so for each row I could run code that would utilize the first and second column data.
This is in c#
Thanks,
Christian
I think the best way of accessing this data is either through the Data Source:
dataGridView.DataSource = someData;
someData.property;
OR, if the user is entering data on the page, you can access from the FindControl method:
name = ((TextBox)dataGridView.Rows[e.RowIndex].FindControl("name")).Text;
In this case, if you've raised an event for a specific row, it will return EventArgs e, with a specific RowIndex. Then you can access the Column values via the ControlID within the column, such as <asp:TextBox id="name" runat="server" /> from .FindControl("name").
The important thing to remember is that you have to cast that object back to the type that it should be from the .FindControl() method.
Remember, it's always a good practice to bind the DataGridView to a data source, and then using the data source to do anything data-related. This keeps you clean from interacting with the datagrid.
foreach(DataGridViewRow row in dataGridView.Rows)
{
//Your code here
}
I have a System.Data.DataTable which I'm binding to a GridView. If the user doesn't have a certain security role, I want to replace the data in certain columns with an "X". Can I do this without looping through all of the rows?
Implement the GridView's RowDataBound event and make the change in there if the user's security role is inadequate.
void CusomGridView_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if(User.Role != "Admin")
{
e.Row.Cells[1].Text = "X";
}
}
Strictly speaking, no. You can use Linq-style extension methods to hide the implementation, but any code in a procedural language is going to involve an iterative loop through the rows. Even the RowDataBound event is fired iteratively by each row; however, it's iterating through the rows anyway, so at least you're not duplicating the looping behavior.
You could also do it on the back end. Pass the userID or role to an sp which returns only viewable columns and 'X' values for columns they can't see and bind the UI to that.
You could simply have a second column available and ready name it "your column2"
filled with x data, you then determine which column is visible based on admin access level.
In VB.NET you cannot change values of each row without recursion.
I'm creating a dynamic GridView from a DataTable that is returned from a stored procedure. I call and bind with the following code:
DataTable dt = Sql.reportData(Convert.ToInt32(Session["userID"]));
this.GridView1.DataSource = dt.DefaultView;
this.GridView1.DataBind();
I need to restyle certain columns but they are not always the same column number, and only have the headers text string to identify it. Is there an easy way to track a column down like this so I can edit its attributes?
Thanks,
Alex
I've run into this myself. You've got to loop through the column names, get the index, and then refer to the index to manipulate the style.
Muhammad is right about the timing, but you won't be searching for a label--it seems you want to style the entire column, right?
http://forums.asp.net/p/1076872/1584635.aspx
the above has several versions of a solution.
The best place to find the control and use it will be in the RowCreated event. RowDataBound should not be used because you dont have to manipulate the data with which the column is being binded. So restyle the elements in the column by searching them in the RowCreated event.
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
e.Row.FindControl("");
}