I am trying to find the last row of my grid, and programmatically add a drop down list to the grid. My code compiles with no issues, but my RowDataBound is never hit when stepping through the code causing the list to never be added. What have I improperly coded?
EDIT
The page loads and you select an option from the drop down list. RefreshdatagridTest is called at that point.
End Of Edit
ASP Code
<div class="CenterGrid">
<asp:DataGrid runat="server" ID="datagridTest" AutoGenerateColumns="false"
Width="500px" CssClass="DataGrids" HorizontalAlign="Center" GridLines="Both"
ShowFooter="true" OnSelectedIndexChanged="datagridTest_SelectedIndexChanged"
OnItemCommand="datagridTest_ItemCommand" RowDataBound="datagridTest_RowDataBound" Visible="true">
<ItemStyle CssClass="row" />
<FooterStyle CssClass="DataGridFooters" />
<HeaderStyle CssClass="DataGridHeaders" />
<Columns>
<asp:BoundColumn DataField="Firefly" HeaderText="Name"></asp:BoundColumn>
<asp:ButtonColumn CommandName="Delete" HeaderText="Delete From List" Text="[Remove]"></asp:ButtonColumn>
</Columns>
</asp:DataGrid>
</div>
C# Code
protected void datagridTest_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowIndex == datagridTest.Items.Count - 1)
{
DropDownList ddl = new DropDownList();
e.Row.Cells[0].Controls.Add(ddl);
}
}
protected void datagridTest_ItemCommand(object source, DataGridCommandEventArgs e)
{
LinkButton lnkbtn = ((LinkButton)e.CommandSource);
if (e.CommandName == "Delete")
{
DataGridItem deletecontrol = (DataGridItem)lnkbtn.NamingContainer;
string bomb = deletecontrol.Cells[0].Text;
RemoveFireflyFromList(bomb);
RefreshdatagridTest();
}
}
How the grid gets data bound
protected void listselect_SelectedIndexChanged(object sender, EventArgs e)
{
RefreshdatagridTest();
}
private void RefreshdatagridTest()
{
dataSet = Run sql procedure to get results;
this.datagridTest.DataSource = dataSet;
this.datagridTest.DataBind();
}
its bit differente since in my exemple i generate new row at runtime but basicaly its how it work;
in my case had to add some size param or control was hidden.
so i just get to set control row's and add the row to the grid.
Mycontrol m = new Mycontrol();
RowDefinition rowDef1 = new RowDefinition();
rowDef1.MaxHeight = m.ExpandedSise;
rowDef1.MinHeight = m.Height;
rowDef1.Height = new GridLength(m.RowDefColapsedSize);
int rownumber = grid2.RowDefinitions.Count;
grid2.RowDefinitions.Insert(rownumber, rowDef1);
Grid.SetRow(m, rownumber);
m.counter.Content = Grid.GetRow(m).ToString();
grid2.Children.Add(m);
Related
I have a DataGridView with a column which with button.
Now I want that with every click on the button the selected row in the DataGridView is copied and inserted directly after the selected row.
I want to duplicate all value of all columns and insert into table of my database this new row.
How can I realize this ?
Many thanks in advance.
On my code below the error is on this line
cgv.Rows.Add(gvRow.Cells[1].Text);
<asp:GridView runat="server" ID="gvCustomers" AutoGenerateColumns="false"
OnSelectedIndexChanged="OnSelectedIndexChanged">
<Columns>
<asp:TemplateField ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:ImageButton ID="btselect" runat="server"
CommandName="Select"
OnClick="Copy"
ImageUrl="/aspnet/img/clone_icon.gif"
ToolTip="Clone row" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="CustomerId" HeaderText="ID" />
</Columns>
</asp:GridView>
protected void Copy(object sender, EventArgs e)
{
GridViewRow gvRow = this.gvProducts.SelectedRow;
DataTable cgv = null;
if (ViewState["Customers"] != null)
{
cgv = ViewState["Customers"] as DataTable;
cgv.Rows.Add(gvRow.Cells[1].Text);
ViewState["Customers"] = cgv;
}
BindData();
}
protected void gvProducts_SelectedIndexChanged(object sender, EventArgs e)
{
this.gvProducts.SelectedRow.BackColor = System.Drawing.Color.Cyan;
}
Ok, you are in a good place in that you attempting to add the data row to the data source, or the datatable that you are persiting in session.
So, I would do it this way:
protected void Copy(object sender, EventArgs e)
{
GridViewRow gvRow = this.gvProducts.SelectedRow;
DataTable cgv = null;
if (ViewState["Customers"] != null)
{
cgv = ViewState["Customers"] as DataTable;
DataRow MyNewRow = cgv.NewRow()
MyNewRow["FirstName"] = gvRow.Cells[2].Text;
MyNewRow["LastName"] = gvRow.Cells[3].Text;
MyNewRow["CustomerID"] = gvRow.Cells[0];
cgv.Rows.Add(MyNewRow);
ViewState["Customers"] = cgv;
}
BindData();
}
Do remember that any template field in the GV needs to use find control, but from what you posted, it does look like using .cells[] collection should work for you.
Its a standard gridview:
<asp:GridView runat="server" ID="gvAlerts" AutoGenerateColumns="false"
DataKeyNames="Id" CssClass="table table-striped table-bordered table-hover"
OnRowDataBound="gvAlerts_RowDataBound"
OnSelectedIndexChanging="gvAlerts_SelectedIndexChanging"
OnRowDeleting="gvAlerts_RowDeleting"
EmptyDataText="There are no alerts to manage."
PageSize="10" AllowPaging="true"
PagerSettings-Position="TopAndBottom"
PagerSettings-Visible="true"
PagerSettings-Mode="NumericFirstLast"
OnPageIndexChanging="gvAlerts_PageIndexChanging">
<Columns>
<asp:BoundField HeaderText="" DataField="ContractEntity"
SortExpression="Supplier" />
<asp:BoundField HeaderText="Reference" DataField="Reference"
SortExpression="Reference" />
<asp:BoundField HeaderText="Date" DataField="Date"
SortExpression="Date" DataFormatString="{0:dd/MM/yyyy}" />
<asp:BoundField HeaderText="Contact Person" DataField="Username"
SortExpression="Username" />
<asp:BoundField HeaderText="End Date" DataField="EndDate"
SortExpression="EndDate" DataFormatString="{0:dd/MM/yyyy}" />
<asp:BoundField HeaderText="Value" DataField="Value"
SortExpression="Value" DataFormatString="R{0:# ### ###.00}" />
<asp:BoundField HeaderText="Category" DataField="ContractCategory"
SortExpression="Category" />
<asp:CommandField ShowSelectButton="true" SelectText="<i class='glyphicon glyphicon-pencil'></i>" />
<asp:CommandField ShowDeleteButton="true" DeleteText="<i class='glyphicon glyphicon-trash'></i>" />
</Columns>
</asp:GridView>
I can't seem to get the paging working right though.
In the code below I've limited the data to 80 records (for testing purposes). It appears that the data does actually page over when I go to a different page, but that's not all it does; there is also always less data returned every time until eventually paging is no longer possible because there aren't enough records to bind to the gridview.
private List<Alert> _alerts { get; set; }
protected void gvAlerts_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
gvAlerts.PageIndex = e.NewPageIndex;
try
{
PageData(e.NewPageIndex);
}
catch (ArgumentNullException ane)
{
// Get all Alerts data again since the collection was apparently
// emptied on postback.
_alerts = GetAlerts();
PageData(e.NewPageIndex);
}
}
private void PageData(int pageIndex)
{
List<Alert> alerts = _alerts;
if (pageIndex >= 2)
{
alerts = _alerts.Skip(pageIndex * gvAlerts.PageSize).Take(gvAlerts.PageSize).ToList();
}
gvAlerts.DataSource = BuildGridViewModel(alerts);
gvAlerts.DataBind();
}
private List<AlertListViewModel> BuildGridViewModel(List<Alert> alerts)
{
var model = new List<AlertListViewModel>();
var u = HttpContext.Current.User.Identity;
using (var db = new ApplicationDbContext())
{
foreach (Alert alert in alerts)
{
// Due to poor database design, these queries are unavoidable.
var contract = db.Contracts.FirstOrDefault(x => x.Id == alert.ContractId);
var category = db.Categories.FirstOrDefault(x => x.Id == contract.CategoryId).Name;
var entity = db.ContractEntities.FirstOrDefault(x => x.Id == contract.ContractEntityId).Name;
model.Add(new AlertListViewModel
{
// Map model properties.
});
}
}
return model;
}
I understand that the .Skip() and .Take() are probably what's shortening the data set but without those, how am I to change the data that is currently visible on the gridview?
I'm missing something here. What is it? How do I get this gridview paging without losing any data?
The downside of the GridVIew is that all the records are retrieved every time and/or all stored in ViewState. So if you want to use Linq create your own paging method. Below a quick example of how this can be done.
If you still want to use the build-in paging, follow the comment of InitLipton
List<Book> books;
int pageSize = 10;
protected void Page_Load(object sender, EventArgs e)
{
//fill the collection
books = fillBooks();
//create the dynamic pager buttons, this needs to be done on every page load
createPager();
//bind the grid for the first time without postback
if (!IsPostBack)
{
bindGrid(0);
}
}
private void bindGrid(int offSet)
{
//bind the right amount of items to the grid
GridView1.DataSource = books.Skip(offSet).Take(pageSize).ToList();
GridView1.DataBind();
}
private void createPager()
{
//loop for every x items in the collection
for (int i = 0; i < (books.Count / pageSize); i++)
{
//create a linkbutton
LinkButton lb = new LinkButton();
//add the properties
lb.Text = (i + 1).ToString();
lb.CommandArgument = i.ToString();
//bind the command method
lb.Command += Lb_Command;
//add the linkbutton to the page
PlaceHolder1.Controls.Add(lb);
//add some spacing
PlaceHolder1.Controls.Add(new Literal() { Text = " " });
}
}
private void Lb_Command(object sender, CommandEventArgs e)
{
//rebind the grid with the next 10 items
bindGrid(Convert.ToInt32(e.CommandArgument) * 10);
}
And the aspx
<asp:GridView ID="GridView1" runat="server" EnableViewState="false"></asp:GridView>
<br />
<br />
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
I have a GridView with a TemplateField column that I put PlaceHolder controls in. During the DataBound event for the GridView I dynamically add a few CheckBoxes to the PlaceHolder. That works fine and displays as expected.
My problem is that during the RowUpdating event the PlaceHolder contains no controls; my CheckBoxes are missing. I also noticed that they're missing during the RowEditing event.
I want to be able to get the values of the CheckBoxes during the RowUpdating event so I can save them to the database.
Here's some example code. I've trimmed out a lot to reduce size, but if you want to see specifics just ask and I'll be happy to add more.
HTML:
<asp:GridView ID="gridView" runat="server" AutoGenerateColumns="False"
ondatabound="gridView_DataBound" onrowupdating="gridView_RowUpdating"
onrowediting="gridView_RowEditing" DataKeyNames="ID">
<Columns>
<asp:TemplateField HeaderText="Countries">
<ItemTemplate>
<asp:PlaceHolder ID="countriesPlaceHolder" runat="server"></asp:PlaceHolder>
</ItemTemplate>
<EditItemTemplate>
<asp:PlaceHolder ID="countriesPlaceHolder" runat="server"></asp:PlaceHolder>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LinkButton ID="editButton" runat="server" CommandName="Edit" Text="Edit"></asp:LinkButton>
</ItemTemplate>
<EditItemTemplate>
<asp:LinkButton ID="updateButton" runat="server" CommandName="Update" Text="Update"></asp:LinkButton>
</EditItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code behind:
// This method works fine, no obvious problems here.
protected void gridView_DataBound(object sender, EventArgs e)
{
// Loop through the Holidays that are bound to the GridView
var holidays = (IEnumerable<Holiday>)gridView.DataSource;
for (int i = 0; i < holidays.Count(); i++)
{
// Get the row the Holiday is bound to
GridViewRow row = gridView.Rows[i];
// Get the PlaceHolder control
var placeHolder = (PlaceHolder)row.FindControl("countriesPlaceHolder");
// Create a CheckBox for each country and add it to the PlaceHolder
foreach (Country country in this.Countries)
{
bool isChecked = holidays.ElementAt(i).Countries.Any(item => item.ID == country.ID);
var countryCheckBox = new CheckBox
{
Checked = isChecked,
ID = country.Abbreviation + "CheckBox",
Text = country.Abbreviation
};
placeHolder.Controls.Add(countryCheckBox);
}
}
}
protected void gridView_RowEditing(object sender, GridViewEditEventArgs e)
{
// EXAMPLE: I'm expecting checkBoxControls to contain my CheckBoxes, but it's empty.
var checkBoxControls = gridView.Rows[e.NewEditIndex].FindControl("countriesPlaceHolder").Controls;
gridView.EditIndex = e.NewEditIndex;
BindData();
}
protected void gridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
// EXAMPLE: I'm expecting checkBoxControls to contain my CheckBoxes, but it's empty.
var checkBoxControls = ((PlaceHolder)gridView.Rows[e.RowIndex].FindControl("countriesPlaceHolder")).Controls;
// This is where I'd grab the values from the controls, create an entity, and save the entity to the database.
gridView.EditIndex = -1;
BindData();
}
This is the article that I followed for my data binding approach: http://www.aarongoldenthal.com/post/2009/04/19/Manually-Databinding-a-GridView.aspx
You need to call your BindData() method on page load.
"Dynamic controls or columns need to be recreated on every page load, because of the way that controls work. Dynamic controls do not get retained so you have to reload them on every page postback; however, viewstate will be retained for these controls."
See Cells in gridview lose controls on RowUpdating event
Also in the article you linked, there is an ItemTemplate and an EditItemTemplace because they have different displays, i.e. read only and editable. Yours are the same so I think you could simplify your design:
<asp:GridView ID="gridView" runat="server" AutoGenerateColumns="False" DataKeyNames="ID" ondatabound="gridView_DataBound">
<Columns>
<asp:TemplateField HeaderText="Countries">
<ItemTemplate>
<asp:PlaceHolder ID="countriesPlaceHolder" runat="server"></asp:PlaceHolder>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LinkButton ID="editButton" runat="server" Text="Edit" onclick="editButton_Click" ></asp:LinkButton>
<asp:LinkButton ID="updateButton" runat="server" Text="Update" onclick="updateButton_Click" ></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code Behind:
protected void gridView_DataBound(object sender, EventArgs e)
{
// Loop through the Holidays that are bound to the GridView
var holidays = (IEnumerable<Holiday>)gridView.DataSource;
for (int i = 0; i < holidays.Count(); i++)
{
// Get the row the Holiday is bound to
GridViewRow row = gridView.Rows[i];
// Get the PlaceHolder control
var placeHolder = (PlaceHolder) row.FindControl("countriesPlaceHolder");
var countryCheckBox = new CheckBox
{
Checked = true,
ID = "auCheckBox",
Text = "Aus",
Enabled = false
};
placeHolder.Controls.Add(countryCheckBox);
var editButton = (LinkButton)row.FindControl("editButton");
editButton.CommandArgument = i.ToString();
var updateButton = (LinkButton)row.FindControl("updateButton");
updateButton.CommandArgument = i.ToString();
updateButton.Visible = false;
}
}
protected void editButton_Click(object sender, EventArgs e)
{
LinkButton editButton = (LinkButton) sender;
int index = Convert.ToInt32(editButton.CommandArgument);
GridViewRow row = gridView.Rows[index];
// Get the PlaceHolder control
LinkButton updateButton = (LinkButton)row.FindControl("updateButton");
updateButton.Visible = true;
editButton.Visible = false;
CheckBox checkbox = (CheckBox)row.FindControl("auCheckBox");
if (checkbox != null)
{
checkbox.Enabled = true;
// Get value and update
}
}
protected void updateButton_Click(object sender, EventArgs e)
{
LinkButton updateButton = (LinkButton)sender;
int index = Convert.ToInt32(updateButton.CommandArgument);
GridViewRow row = gridView.Rows[index];
// Get the PlaceHolder control
LinkButton editButton = (LinkButton)row.FindControl("updateButton");
editButton.Visible = true;
updateButton.Visible = false;
CheckBox checkbox = (CheckBox)row.FindControl("auCheckBox");
if (checkbox != null)
{
// Get value and update
checkbox.Enabled = false;
}
}
If you want to be it enabled from the get go, just remove the enabled checks and you can delete your edit button.
Hope that helps.
I am using a GridView that has many rows with the same values, so I grouped these rows using GridViewHelper class. Now I want to put a separate line between each grouping values. How to do that?
The following snapshot shows you the current situation:
I wasn't sure if you wanted to style them when rendered or when you click the row. So here is both. You can decide what styles you want. :-)
Set the appropriate attributes in your ASP.NET GridView control for controlling CSS:
<asp:GridView ID="gridviewid"
runat="server"
CssClass="gridview"
AutoGenerateSelectButton="True"
GridLines="None"
AllowPaging="true"
PageSize="10">
<HeaderStyle CssClass="gridViewHeader" />
<RowStyle CssClass="gridViewRow" />
<AlternatingRowStyle CssClass="gridViewAltRow" />
<SelectedRowStyle CssClass="gridViewSelectedRow" />
<PagerStyle CssClass="gridViewPager" />
</asp:GridView>
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.CssClass = "cssClass";
}
}
You can try something like...
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.RegisterGroup("yourGroupName", true, true);
helper.GroupHeader += new GroupEvent(helper_GroupHeader);
}
private void helper_GroupHeader(string groupName, object[] values, GridViewRow row)
{
if (groupName == "yourGroupName" )
{
row.Cells[0].Text = "<br />" + row.Cells[0].Text;
}
}
You should also be able to insert a new one before row instead of just adding a break line to the cell if that's what you want.
Please consider the values in comments which I got in debug mode:
protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
int selected = FilesGrid.SelectedIndex; // selected = 2
FilesGrid.DataBind(); //added after feedback in comments. it makes no change
int count = FilesGrid.Rows.Count; // count = 0
GridViewRow row = FilesGrid.Rows[selected]; // row = null
GridViewRow row0 = FilesGrid.Rows[0]; // row = null
}
I came to this code while investigating why SelectedValue gives null in this event handler (the DataKeyNames parameter was set for sure).
Can anybody explain how it is possible?
Thank you in advance.
PS.
Here is my aspx code:
<asp:GridView ID="FilesGrid" runat="server" AutoGenerateColumns="False"
AutoGenerateSelectButton="True"
onselectedindexchanged="FilesGrid_SelectedIndexChanged"
style="margin-top: 0px" >
<Columns>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="Name" HeaderText="Name" />
<asp:BoundField DataField="Length" DataFormatString="{0:N0}"
HeaderText="Size in Bytes" HtmlEncode="False" />
</Columns>
</asp:GridView>
Here is how I bind data:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string [] dd = {"FullName"};
FilesGrid.DataKeyNames = dd;
string appPath = Request.PhysicalApplicationPath;
DirectoryInfo dirInfo = new DirectoryInfo(appPath);
FileInfo[] files = dirInfo.GetFiles();
FilesGrid.DataSource = files;
FilesGrid.DataBind(); }
}
Y copy pasted your code, delete this line FilesGrid.DataBind() in the FilesGrid_SelectedIndexChanged, i see that are 2 methods that you are not posting, that aren't in the code that you posted the onselectedindexchanging, onrowdeleting events, comment them from the aspx and see if it works, or se if that events are not doing something tricking, that are deleting the rows in your GridView.
Tell me if it works
I did this
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string[] dd = { "FullName" };
FilesGrid.DataKeyNames = dd;
string appPath = Request.PhysicalApplicationPath;
DirectoryInfo dirInfo = new DirectoryInfo(appPath);
FileInfo[] files = dirInfo.GetFiles();
FilesGrid.DataSource = files;
FilesGrid.DataBind();
}
}
protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
int selected = FilesGrid.SelectedIndex; // selected = 2
//FilesGrid.DataBind(); //added after feedback in comments. it makes no change
int count = FilesGrid.Rows.Count; // count = 0
GridViewRow row = FilesGrid.Rows[selected]; // row = null
GridViewRow row0 = FilesGrid.Rows[0]; // row = null
}
protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
}
protected void FilesGrid_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
}
The aspx code.
<asp:ScriptManager ID="ScriptManager1" runat="server" EnableScriptGlobalization="true"
AsyncPostBackTimeout="0" EnableScriptLocalization="true">
</asp:ScriptManager>
<asp:UpdatePanel ID="upPanel" runat="server">
<ContentTemplate>
<asp:GridView ID="FilesGrid" runat="server" AutoGenerateColumns="False" AutoGenerateSelectButton="True"
OnRowDeleting="FilesGrid_RowDeleting" OnSelectedIndexChanged="FilesGrid_SelectedIndexChanged"
Style="margin-top: 0px" OnSelectedIndexChanging="FilesGrid_SelectedIndexChanging">
<Columns>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="Name" HeaderText="Name" />
<asp:BoundField DataField="Length" DataFormatString="{0:N0}" HeaderText="Size in Bytes"
HtmlEncode="False" />
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
remove the FilesGrid.DataBind(); //added after feedback in comments. it makes no change
When I add that statement to my code I am getting the error. after removing that please try again. If not working please share the
protected void FilesGrid_SelectedIndexChanging(object sender, GridViewSelectEventArgs e) method code, may be something wrong with that section
The issue was resolved by commenting out the line // if (!Page.IsPostBack). It looks like the data source is lost somehow during the postback. The whole seems to be a local bug with ViewState, because this behavior was not observed by other users. My special thanks to Tim Schmelter and naveen.
You can keep Page.IsPostBack in Page_Load, I was having the same issue and it turns out the grid was not in ViewState in my case. If you use Linq query in your controller you will want to add this property to the grid so it will keep refreshing properly:
<asp:Panel EnableViewState="True">