I've been writing an extension to grid view to add grouping rows to a gridview, by watching for when the desired item changes in the RowDataBoundEvent. So far it looks a little like this:
protected override void OnRowDataBound(GridViewRowEventArgs args)
{
if (args.Row.RowType == DataControlRowType.DataRow)
{
object dataitem = args.Row.DataItem;
string currentValue = dataitem.GetType().GetProperty(GroupingColumn).GetValue(dataitem, null).ToString();
if (string.IsNullOrEmpty(previousValue) || currentValue != previousValue)
{
int currentRowIndex = args.Row.RowIndex + rowsAdded;
int colspan = this.Columns.Count - 1;
Label label = new Label { Text = currentValue, CssClass = "rowLabel" };
TableCell cell = new TableCell { ColumnSpan = colspan, CssClass = "groupHeader" };
GridViewRow newRow = new GridViewRow(currentRowIndex, currentRowIndex, DataControlRowType.DataRow, DataControlRowState.Normal);
cell.Controls.Add(label);
newRow.Cells.Add(cell);
((Table)args.Row.Parent).Controls.AddAt(currentRowIndex, newRow);
previousValue = currentValue;
rowsAdded++;
}
}
base.OnRowDataBound(args);
}
The issue is that when I click a paging button, the paging event doesn't fire, and the extra rows added become empty rows. The page postbacks, and runs through page load etc, but never through the paging event. I have custom paging built into this extension as well, and the click event for my paging buttons doesn't fire either. It's not until the second click on the paging button that paging occurs.
I think this is happening because the paging row gets constructed before the rowdatabound event, during which paging buttons are created and their event handler's assigned. However, adding rows to the underlying table removes these handlers. Anyone have any idea how to ensure the custom paging buttons fire?
Edit 1: To clarify, adding to the data source then rebinding won't work, as there is no extra data to add. The effect I'm attempting to achieve is something like this:
|Group 1 |
|Item 1 |Item 2 |Item 3 |
|Item 4 |Item 5 |Item 6 |
|Group 2 |
|Item 7 |Item 8 |Item 9 |
Found a solution on an answer to a similar question. The trick is to add rows in the PrepareControlHierarchy event. Also, make sure your row type is "Header", otherwise there can be some nasty side effects with custom paging.
See Add Gridview Row AFTER Header
Related
What I've done so far
DataSet dataSet = new DataSet();
dataSet.ReadXml(dialog.FileName);
dataGridView1.DataSource = dataSet.Tables[0];
MessageBox.Show(this.dataGridView1.Columns["Visible"].Index.ToString());//To hide -returns 0
foreach (DataGridViewRow dr in dataGridView1.Rows)
{
if (dr.Cells[0].Value.ToString() == "False")
{
dr.Visible = false;
}
}
The gridview
I'm trying to hide the entire Row where the Visible Column value is False
After some research, I am confident that you will be better off “filtering” the grids DataSource as opposed to setting the individual grid row’s Visible property to false.
The biggest problem you will have in doing this… is that you CAN NOT set the grids CurrentRow Visible property to false. This will throw an exception that complains along the lines of …
”Row associated with the currency manager's position cannot be made invisible”
… basically this is saying the grid's CurrentRow cannot be invisible.
Considering this, it would appear that this approach may not work since at least ONE (1) row in the grid will be the CurrentRow and your code will fail if the grid's CurrentRow “Visible” cell is set to “False.”
In addition, to exploit the testing parameters… what if ALL the rows are “False”? … In that case the exception is guaranteed since at least ONE of the rows will be the CurrentRow.
Hopefully, this may explain “why” your code may work some times and not at other times.
Therefore, I suggest a simple solution that avoids the grids currency manager all together. This can be done by filtering the grids DataSource. Something like the button click event below…
private void button1_Click(object sender, EventArgs e) {
DataView dv = new DataView(dataSet.Tables[0]);
dv.RowFilter = "Visible = True"; // <- filter rows that have True in the Visible column
dataGridView1.DataSource = dv;
}
It is unclear “where” the posted code in your question is executed, however in my solution above it will make things easier if you make dataSet or at least dataSet.Tables[0] a “GLOBAL” variable. Reason being that when you use the DataView.RowFilter and then set the grids data source to the DataView… then unless you have access to the original table dataset.Tables[0]… you will not be able to “un-filter” the grid and instead you would need to re-query the DB. I hope that makes sense. Good Luck.
The main problem here, I think, is that you are replacing the Visible value of the row, instead of the row of the Datagrid. Replace the foreach with a for:
for(int i=0; i <= dataGridView1.Rows.Count();i++) {
dataGridView1.Rows[i].Visible = Convert.ToBoolean(dataGridView1.Rows[i].Cells[0].Value);
}
What is the value of dr.Cells[0].Value.ToString() when you get that row? Check it with debugger and quickwatch. Maybe is not "False" as you show it.
The main idea is get any kind of false with Convert. And also you don't need the if at all.
if (Convert.ToBoolean(dr.Cells[0].Value))
{
dr.Visible = false;
}
And also you don't need the if at all.
dr.Visible = Convert.ToBoolean(dr.Cells[0].Value);
You may use the DataGridView CellPainting event.
Everytime a cell in dataGridView1 needs repainting the event is fired.
The good thing is that this event will fire when the dataGridView1 is initialized and when the user leaves a cell. Hence this solution will remove the arbitrary rows when the DataGridView is initializing (and then remove any loaded rows with "False" in column 0) but also remove any rows that are changed to "False" by the user during run time.
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.ColumnIndex < 0 || e.RowIndex < 0)
return;
if (dataGridView1.Rows[e.RowIndex].Cells[0].Value == null) //Return if cell value is null
return;
if (e.ColumnIndex == 0) //Current cell to paint is in visible column
{
DataGridViewRow currentRow = dataGridView1.Rows[e.RowIndex]; //Row of current cell
if (currentRow.Cells[0].Value.ToString() == "False")
{
currentRow.Visible = false;
}
}
}
Either add the event at the events list in the Design view or add it directly to the designer class for the control containing dataGridView1
//
// dataGridView1
//
...
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.dataGridView1_CellPainting);
...
I want to implement GridControl for my project but I realize Syncfusion GridControl is little more tough than Windows controls.
my requirement is like that: Suppose I have three columns and 15 rows in my gridControl and in first column of first row I want to write some hardcoded input string and in second column of first row I want to add CheckBox.
Kindly suggest me how to bind cells with CheckBox so that it would work dynamically while scrolling.
I also go through from Here:
Query 1
In first column of first row I want to write some hardcoded input string
Suggestion 1
The cell value for a particular cell can be set by using CellValue property of cell style. Please refer the below code,
this.gridControl1[1, 1].CellValue = "Sample";
Suggestion 2
The cell value can also be set by using Text property of cell style. Please make use of below code,
this.gridControl1[2, 1].Text = "Data";
Suggestion 3
To set the cell value for a particular cell, the QueryCellInfo event can also be used. Please refer below code,
//Event Triggering
this.gridControl1.QueryCellInfo += GridControl_QueryCellInfo;
//Event Customization
private void GridControl_QueryCellInfo(object sender, GridQueryCellInfoEventArgs e)
{
if(e.RowIndex==1 && e.ColIndex==1)
{
e.Style.CellValue = "Sample Name";
}
if(e.RowIndex==2 && e.ColIndex==1)
{
e.Style.Text = "Sample ID";
}
}
Query 2
In second column of first row I want to add Checkbox
Suggestion 1
To set the cell type as CheckBox for a particular cell, the CellType property can be used and the name for the CheckBox can be set by using Description property. Please refer the below code,
this.gridControl1[1, 2].CellType = GridCellTypeName.CheckBox;
this.gridControl1[1, 2].Description = "CheckBox";
The CheckBox can be checked or unchecked based on the cell value by defining the CheckBoxOptions property of the cell.
this.gridControl1[1, 2].CheckBoxOptions = new GridCheckBoxCellInfo("True","False","False",true);
this.gridControl1[1, 2].CellValue = "True";
Suggestion 2
To set Cell type as CheckBox for a particular cell, the QueryCellInfo event can also be used. Please refer the below code,
//Event Triggering
this.gridControl1.QueryCellInfo += GridControl_QueryCellInfo;
//Event Customization
private void GridControl_QueryCellInfo(object sender, GridQueryCellInfoEventArgs e)
{
if(e.RowIndex==2 && e.ColIndex==2)
{
e.Style.CellType = GridCellTypeName.CheckBox;
e.Style.Description = "CheckBox";
e.Style.CheckBoxOptions.CheckedValue = "True";
e.Style.CellValue="True";
}
}
Screenshot
Sample Link
UG Link
Dashboard sample
\Syncfusion\EssentialStudio<Installed Version>\Windows\Grid.Windows\Samples\ Cell Types\Interactive Cell Demo\CS
I have a webpage with a gridview attached to it. The gridview allows the user to update individual records. The gridview looks like this:
JobSiteID JobSite1
1 13-03
2 13-04
3 13-06
4 13-09
5 13-15
I created the following record updating event handler:
protected void changeJobSiteRecordsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
GridViewRow row = changeJobSiteRecordsGridView.Rows[e.RowIndex];
TextBox txtJobSite = row.FindControl("txtJobSite") as TextBox;
if (txtJobSite != null)
{
using (ABCEntities4 Context = new ABCEntities4())
{
int jobSiteID = Convert.ToInt32(changeJobSiteRecordsGridView.DataKeys[e.RowIndex].Value);
JobSite obj = Context.JobSites.First(x => x.JobSiteID == jobSiteID);
obj.JobSite1 = txtJobSite.Text;
Context.SaveChanges();
changeJobSiteRecordsGridView.EditIndex = -1;
changeJobSiteRecordsGridView.DataSource = Context.JobSites;
changeJobSiteRecordsGridView.DataBind();
}
}
}
Here's my problem:
When I select to update, say row #2, on the first line, the local "row" variable indicates that the RowIndex == 1.
However, in the second line, I expect txtJobSite variable to be populated with "13-04" but VS assigns "null" to the variable.
As a result, the code flows over the if then statement below which isn't what was intended.
Any help would be greatly appreciated.
Thanks.
Check the row's cells property like this:
row.Cells[1].Controls[0]
for the text box. If the '0' index doesn't work, try the 1 index. Then your code would look something like this:
TextBox txtJobSite = (TextBox)row.Cells[1].Controls[1]
I remember running into a similar problem with FindControl. This way, you explicitly find the cell and then the control in the cell.
At first, I want to talk a little about normal binding (with standard property such as Text of a textbox). The sample grid here has only 2 rows (for simplicity). Suppose I have a table of 2 rows (myDataTable) with 2 columns (ID and Name), a DataGridView (myGrid) and a TextBox (myTextBox). Here is the code for binding data:
myGrid.DataSource = myDataTable;
myTextBox.DataBindings.Add("Text", myDataTable, "Name");
After binding data, when the selection changes in the grid, the info is updated automatically to the control TextBox, for example, the 2 rows are:
ID | Name
1 .NET
2 Java
At first, the selection in grid is at index 0, the Text of myTextBox is ".NET", moving the selection to next position (at index 1), the Text of myTextBox is "Java", moving again and again, forward and backward, it works OK as I expect. But now I have a control with a custom property called List, this is type of List and is readonly. I want to bind it to a column of table (for example, Name), I do the same binding rule, however add a little custom Parse to format the correct string before updating to the datasource (myDataTable) because my custom property is type of List while my Name column is type of string, here is the binding code:
Binding bind = new Binding("List", myDataTable, "Name"){
ControlUpdateMode = ControlUpdateMode.Never //Because my List property is readonly
};
//formating string data before updating to the datasource
bind.Parse += (s,e) => {
List<string> data = (List<string>) e.Value;
if(data.Count == 0) e.Value = DBNull.Value;
else e.Value = string.Join(",",data.ToArray());//format as comma separated string
};
myCustomControl.DataBindings.Add(bind);
In this case, suppose myDataTable currently has no data at column Name like this:
ID | Name
1 <DBNull.Value> <--- current index
2 <DBNull.Value>
After running the demo, the current selection index in the grid is 0, I try changing the value of myCustomControl property List (the Items, not the reference), for example, update it like this:
myCustomControl.List.Add(".NET");
myCustomControl.List.Add("Java");
Then, moving the selection in grid to the next position (index 1), the value ".NET,Java" is updated to the datasource in row 0 at column Name, like this:
ID | Name
1 .NET,Java
2 <----- current index
Now if I move the selection back to index 0, the value at column Name in row 1 is also updated to ".NET,Java" like this:
ID | Name
1 .NET,Java <----- current index
2 .NET,Java
Which is not what I want. I mean the value should be updated via control myCustomControl. Here is what I want:
ID | Name
1 .NET,Java <----- current index
2
I can understand that, at the time moving back from index 1 to index 0, the value of List property is still a List with 2 items (".NET" and "Java") and so after the moving, this is updated to the cell at column Name in row 1. I'm finding how to reset that value of List property after it's updated to the cell at column Name in row 0 so that when the selection is at index 1, it's already empty. I'v tried changing the Parse event handler to the following but no good shake:
bind.Parse += (s,e) => {
List<string> data = (List<string>) e.Value;
if(data.Count == 0) e.Value = DBNull.Value;
else e.Value = string.Join(",",data.ToArray());//format as comma separated string
//I think at here, the value has been already updated to the datasource
//and I can perform the reset
myCustomControl.List.Clear();
};
But it seems to Clear before the value is updated to the datasource and so there is no value udpated to the datasource (instead of ".NET,Java", it's a DBNull.Value).
Then I have also tried this:
bind.BindingComplete += (s,e) => {
if(e.BindingCompleteContext == BindingCompleteContext.DataSourceUpdate)
myCustomControl.List.Clear();
};
I thought, it should check if the data is updated to the datasource, the List can be clear. I have also tried some flag to mark as true before clearing and reset it to false after clearing, use this flag to control the flowing in bind.Parse but it did nothing.
Do you have any idea to solve this problem? Your help would be highly appreciated! Thank you.
I've found the solution myself. In fact I can't reset the List anyway, this will update the underlying datasource when switching between rows in the grid. The key idea here is initially set DataSourceUpdateMode to DataSourceUpdateMode.Never, then whenever the List is about to change, turn the DataSourceUpdateMode to DataSourceUpdateMode.OnPropertyChanged. In the Parse event handler, after the parsing is done, reset DataSourceUpdateMode to DataSourceUpdateMode.Never. And that works greatly. The underlying datasource is updated only when user changing the control's value (List) by typing or selecting ,...
Here is all the code:
Binding bind = new Binding("List", myDataTable, "Name"){
ControlUpdateMode = ControlUpdateMode.Never, //Because my List property is readonly
DataSourceUpdateMode = DataSourceUpdateMode.Never//This will be turned on when preparing to change the List's value
};
//formating string data before updating to the datasource
bind.Parse += (s,e) => {
List<string> data = (List<string>) e.Value;
if(data.Count == 0) e.Value = DBNull.Value;
else e.Value = string.Join(",",data.ToArray());//format as comma separated string
//At here reset the DataSourceUpdateMode to Never
//We can also do this in BindingComplete event handler with BindingCompleteContext = BindingCompleteContext.DataSourceUpdate
myCustomControl.DataBindings[0].DataSourceUpdateMode = DataSourceUpdateMode.Never;
};
myCustomControl.DataBindings.Add(bind);
myCustomControl has a method to update/populate the new items for the List property called UpdateList(), we have to set DataSourceUpdateMode to OnPropetyChanged at the very beginning of the method like this:
public void UpdateList(){
if(DataBindings.Count > 0) DataBindings[0].DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
//The remaining code for populating/updating new items goes below
....
}
And that's all, very clean. Hope this will help someone who will encounter the same situation with me.Thank you!
I realize this is an easy question, but despite searching I can't find anything specific towards my problem.
I have a gridview populated with 9 or so columns. I want to both change the column names and edit the number of visible columns. So instead of
| x | y | z |
2 6 7
I'd like
|new x|new z|
2 7
I realize that I can manually edit the column names and set them to visible or not, but is there a way to do something like: if (column = y) then (display column) and (column name = new y)?
Much appreciated.
You can handle the GridView.RowDataBound event to modify the columns when they are bound and apply any changes you want at that point.
you could do something like this inside the page load event, or grid load:
foreach(BoundField b in grid.Columns)
{
if(b.HeaderText == 'y')
{
b.HeaderText = "new y";
} else {
b.Visible = false;
}
}