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.
Related
I have a GridView Control that displays data from a table on Page_Load as well as a progress bar in one column and a button in another.
It then loops through each row of the Table and depending on the values, hides said button (i.e. if the 1st column value = "Open" then the button in the 8th column is hidden). It uses a simple foreach loop;
foreach (GridViewRow Row in MyGridView.Rows)
{
if(Row.Cells[0].Text == "Open")
{
Row.Cells[7].Text = "";
}
}
This is working absolutely fine on page load... unfortunately once the user sorts the data by the column values it doesn't work. I can get the event to fire (testing between OnSort and OnSorting amongst other events on the board) but it isn't actually making any changes to the table.
It seems the issues lays somewhere in how I'm attempting to initiate it... does anyone have any ideas?
Thanks in advance.
Managed to work this out through more testing. For anyone with a similar problem, use the DataGridViews OnPreRender Event.
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.
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 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
}
Okay big brains here's something that's more of a challenge than a requirement. I am a bit stumped. I usually just need a prod in the right direction, so get your prodding sticks ready.
I have a tabcontrol covered in textboxes. I want to perform a check of the contents of all the textboxes during the SelectedIndexChanged event on a listview on the same form. If one of the textboxes has data different from a DataTable row - represented by the ListView Item - I want it to ask if the user would like to keep the change they just made. If nothing has changed I want it to just change the selection.
So obviously I'm comparing the contents of the text boxes against associated columns in the datarow.
I could just brute force the check and do each individual check one at a time. I'd prefer to come up with some clever algorithmic way of cycling through the tabcontrol textboxes and checking the values against the columnar values.
Any suggestions?
EDIT: I like the "cleverly named textboxes" solution below best, although both are good. If no one else has a better idea in the next 14 days the textbox answer gets the green.
Give the textboxes a clever name as in a portion of the name is the column/row name.
Group the textbox controls an loop through them. For each control, get the (portion)name and use that as a reference to your datatable. Check the values.
If I'm understanding you right, you want to avoid comparing every textbox on every change, in favour of just checking the textboxes that are changed, driven by the SelectedIndexChanged event of the ListView control. Is that right?
Well, DataRows and DataTables already have row versioning and rollbacks implemented, so if you bind the text boxes to the underlying row (either by writing events to write back on change/lose focus or by using an automated mechanism to accomplish the same task), then check the RowState property on SelectedIndexChanged. If the RowState is anything other than unchanged, prompt the user to save. If he saves, commit the changes, otherwise reject them.
So, for example, you'd want something like this in your SelectedIndexChanged event handler:
if (row.RowState == DataRowState.Modified) {
// prompt for user input
if (promptResult == PromptResult.Save) {
row.AcceptChanges();
}
else {
row.RejectChanges();
}
}