I have at the moment a problem with RowUpdating from a GridView and accessing the new values. I add a dynamic DataTable to my GridView.DataSource and bind to it. If I use the update button nothing happens and I get back to my normal GridView.
Here's my Page_Load event:
protected void Page_Load(object sender, EventArgs e)
{
Control test = GetPostBackControl(Page);
if (Page.IsPostBack)
{
if ((test.ID == "SumbitSearch") && (DropDownListFrom.Text != "") && (DropDownListTo.Text != "") && (SearchField.Text != ""))
{
DataTable result = new DataTable();
string from = null;
string to = null;
switch (DropDownListFrom.SelectedIndex)
{
case 0:
from = DropDownListFrom.Items[0].Value;
break;
case 1:
from = DropDownListFrom.Items[1].Value;
break;
case 2:
from = DropDownListFrom.Items[2].Value;
break;
}
switch (DropDownListTo.SelectedIndex)
{
case 0:
to = DropDownListTo.Items[0].Value;
break;
case 1:
to = DropDownListTo.Items[1].Value;
break;
case 2:
to = DropDownListTo.Items[2].Value;
break;
}
result = LoadGridView(from, to);
GridViewResult.DataSource = result;
Session["Result"] = result;
GridViewResult.DataBind();
GridViewResult.Columns[0].Visible = true;
GridViewResult.Columns[1].Visible = true;
GridViewResult.HeaderRow.Cells[0].Width = Unit.Pixel(110);
GridViewResult.HeaderRow.Cells[1].Width = Unit.Pixel(60);
GridViewResult.HeaderRow.Cells[3].Text = "Nach: " + from;
GridViewResult.HeaderRow.Cells[4].Text = "Von: " + to;
}
}
else
{
GridViewResult.DataBind();
}
}
Later, the GridView should only appear if both DropDownLists are used and the SearchField is not empty. I also check if the button which execute the PostBack is the search button.
Here is what I added to the RowUpdating EventHandler:
protected void TaskGridViewResult_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
GridViewResult.DataSource = (DataTable)Session["Result"];
for (int i = 0; i < GridViewResult.Columns.Count; i++)
{
DataControlFieldCell cell = GridViewResult.Rows[e.RowIndex].Cells[i] as DataControlFieldCell;
GridViewResult.Columns[i].ExtractValuesFromCell(e.NewValues, cell, DataControlRowState.Edit, true);
}
GridViewResult.EditIndex = -1;
DataBind();
}
What is going wrong?
Last Change: More Information (#jwiscarson)
I’m sorry, I was in hurry. I will try to give you a better view.
In that project, the users could select 2 categories, enter a search string and the result will show in a GridView. The RowUpdating stuff is for the "admin user".
Here is my problem, if the "admin user" click on edit, change the value of the cell and execute the update, the value would not changed in the DataTable.
Without seeing your markup, it's hard for […] based on some search
criteria?
I add GridViewResult as markup in my project.
I'm also not sure why […] within that button's Click event.
Yes, I thought that too! But I read in a blog it is better to put the code into Page_Load(), if you are working with GridViews. I believed him. Is it okay, to put that code into the click event handler from my button? I'm very new to that. I just could read lots of blogs, msdn and ask here.
TaskGridViewResult_RowUpdating is also added as markup to my code. Could I also create this and the GridView in my CreateChildControls()?
As markups I have two DropDownLists, a TextBox for the search string, a search submit Button and the GridView.
At the moment, i havent the exactly code here but the GridView looks like:
<asp:GridView ID="GridView1" runat="server"
OnRowUpdating="TaskGridViewResult_RowUpdating">
<!-- // More of that... -->
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:CommandField ShowDeleteButton="True" />
</Columns>
</asp:GridView>
I could give you more details tomorrow.
Maybe I forgot this. Before I could click the update button and execute the TaskGridViewResult_RowUpdating event to change the value of the cell. I set the row editable with:
protected void TaskGridViewResult _RowEditing(object sender, GridViewEditEventArgs e)
{
GridViewResult.EditIndex = e.NewEditIndex;
DataBind();
}
Next Change:
I added to the GridView few TemplateFields like:
<asp:TemplateField HeaderText="test">
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("from") %>'> </asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("from") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
This is it, I have access to the new value with:
string test = ((TextBox)row.FindControl("TextBox1")).Text;
in TaskGridViewResult_RowUpdating()
Now, I just have to bind the template dynamically with the values from the dropdown list, and that’s it I think.
Thanks!
Hoo boy.
You didn't ask this in your question, but you've written some smelly code here. I'll try to address your question, but I'm also going through some of the other problems here.
Without seeing your markup, it's hard for me to tell if you've created GridViewResult programmatically or if it's in your markup as well. I'm not exactly sure what TaskGridViewResult is either -- is this an event for GridViewResult? Do you have another GridView on your page, and you want to show GridViewResult based on some search criteria?
I'm also not sure why you put all of your GridView binding code in Page_Load. I see that you're checking GetPostBackControl to find the control that caused the PostBack -- this is a code smell. If your users have to click a specific "Search" button, you should isolate your GridView binding code within that button's Click event.
Anyway, as to TaskGridViewResult_RowUpdating:
It looks like you're trying to update GridViewResult's old values to the new values by setting its DataSource and then iterating through its data. You can't do that. What happens when you change your RowUpdating event to:
protected void TaskGridViewResult_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
GridViewResult.DataSource = (DataTable)Session["Result"];
GridViewResult.DataBind();
}
Now, other asides:
Your switch statements are code smells. DropDownLists expose a property called SelectedValue -- if you have some default value that isn't a valid selection (like the first option in your DropDownList), then you can change your original code from:
if ((test.ID == "SumbitSearch") && (DropDownListFrom.Text != "") && (DropDownListTo.Text != "") && (SearchField.Text != ""))
{
string from = string.Empty;
string to = string.Empty;
switch (DropDownListFrom.SelectedIndex)
{
case 0:
from = DropDownListFrom.Items[0].Value;
break;
/* other cases */
}
switch (DropDownListTo.SelectedIndex)
{
case 0:
to = DropDownListTo.Items[0].Value;
break;
/* other cases */
}
/* snip */
}
to:
if (test.ID == "SubmitSearch" && DropDownListFrom.SelectedIndex > 0 && DropDownListTo.SelectedIndex > 0 && SearchField.Text != "")
{
string from = DropDownListFrom.SelectedValue;
string to = DropDownListTo.SelectedValue;
/* snip */
}
If you can, please post your markup. I can offer you some more specific help or advice if I can tell what Controls you have on your page, and how they interact.
Edit: I should also add that you should get in the habit of using String.IsNullOrEmpty(string variable), rather than comparing to "". Although this isn't as big of a problem when you're referencing the text from web controls, getting in the habit now will prevent you from serious headaches and inefficiently checking for null and empty later. If you're in .NET 4.0, you should have access to IsNullOrWhiteSpace(), which is (in my opinion) more useful on the web, where users are more likely to try to enter junk data.
Edit 2: In response to your additional details above:
In your RowUpdating event, you need to pull the new values out of the front-end controls, and then re-bind your data. It looks like your source code is pretty close to the MSDN source code here. The difference between your code and the code on MSDN is:
- The MSDN code takes the new data from the edited row and updates the appropriate data in the Session variable with that new data.
- The MSDN code has a BindData() function -- I'm not sure if this is what your DataBind() function is, however.
- The MSDN code updates the GridView by re-binding the GridView to the appropriate data in the Session.
Frankly, I have some issues with the MSDN code. I really, really hate seeing code like: dt.Rows[row.DataItemIndex]["Id"] = ((TextBox)(row.Cells[1].Controls[0])).Text; To me, this is garbage, because it's so highly coupled to the order of your cells.
If I had written the code, I would explicitly create all the front-end controls and use a binding command (if you aren't familiar, it looks like <%# DataBinder.GetPropertyValue(Container.DataItem, "FieldName") %> in the markup -- see more details here). Then, in my RowUpdating event, I'd use e.Row.FindControl("controlName"), and update the Session data from that.
Honestly, if your back-end data isn't changing on a regular basis, I don't see any reason to programmatically create your GridViews. It over-complicates your C# code to go through these steps yourself when you can just set it up once in your markup. If you need to hide one GridView, you can always set its Visible property to false. Then, when you're ready to show it, set Visible = true.
Finally, I've spread my GridView code across so many events (button clicks, RowDataBound events from other GridViews/Repeaters, etc.), I can't imagine why someone would suggest that you only put it in Page_Load.
Related
Picture first to show the interaction with the interface...
This is an interface that I am building which enables the editing of one column in the database for multiple rows. This function works perfectly except for the fact that after making the first edit you can't go back and make another edit.
When you choose the Edit Selected button to pop up the panel for the second time. The panel doesn't pop up.
The reason why it doesn't pop up is because in the code below is no longer 'true' even though the checkboxes on the GridView remain checked.
if (((CheckBox)grv.FindControl("TagRowChkBx")).Checked == true)
I haven't been able to figure out why the checked value is no longer true the second pass.
When the user selects the 'Save' on the edit panel it touches off a SP which performs the update of the table.
Here the relevant portion of the GridView definition:
<asp:GridView ID="ActVulListGV" runat="server" OnSelectedIndexChanged="ActVulListGV_SelectedIndexChanged" PageSize="25" AlternatingRowStyle-BackColor="#ccffff" PagerSettings-PageButtonCount="25" Width="100%" AutoGenerateColumns="False" DataKeyNames="RecID" AllowPaging="True" AllowSorting="True" HeaderStyle-VerticalAlign="Bottom" HeaderStyle-BackColor="#99CCFF" HeaderStyle-ForeColor="Black" DataSourceID="Vul_DS">
<Columns>
<asp:CommandField ShowEditButton="True" ShowSelectButton="True" SelectText="Details" EditText="Edit Row"></asp:CommandField>
<asp:TemplateField HeaderText="Tag" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:CheckBox runat="server" ID="TagRowChkBx" />
</ItemTemplate>
</asp:TemplateField>
<%-- 13 addtional BoundFields -->
</Columns>
</asp:GridView>
Next here is the segment of code that is checking for the boxes to be checked (or not) which is preventing to allow the panel to become visible after the first go around.
protected void EditSelctedBtn_Click(object sender, EventArgs e)
{
//Read the column select drop down List into Local Varriables
String SelectedColumnItem = ColumnSelectDDL.SelectedItem.ToString();
String SelectedColumnValue = ColumnSelectDDL.SelectedValue.ToString();
List<int> EditRows = new List<int>();
List<string> recordnumber = new List<string>();
int rcounter = 0;
foreach (GridViewRow grv in ActVulListGV.Rows)
{
// Here is where 'true' becomes not true on second pass
if (((CheckBox)grv.FindControl("TagRowChkBx")).Checked == true)
{
rcounter++;
//get current row rowindex if checkbox in it is checked
EditRows.Add(grv.RowIndex);
//get the record number (RecID)
recordnumber.Add(grv.Cells[2].Text.ToString());
}
Hopefully someone might be able to suggest what may be causing this so I can use the function more than once per page display.
Thanks, Ken...
Update 05/23/2017 2:20PM Central US:
This continues to baffle me beyond reason.
I took into account that it might be the post back taking place during the databind of the gridview to display the changes made to the table by the SP. I built a routine to capture the state and reestablish it after the binding is completed. Works well... But I still have the same issue of the value of the checkboxes not reading as they are displaying on the second time around the 'Edit Selected' button.
Since I was only checking for 'true' on reading the value of the checkbox I thought maybe I should attempt to determine what the actual value is so I added a else if it is true and else to catch anything that isn't true or false (See code below).
Did nothing for me as well! so I put a break point in and observed what happened for the second pass...
What I found was that as soon as the first line below executed the flow completely bypassed the remainder of this code. So not only was it not true, it wasn't false, and it wasn't something other than false. I think I've discovered dark matter in C# code!
if (((CheckBox)grv.FindControl("TagRowChkBx")).Checked == true)
{
//get current row rowindex if checkbox in it is checked
EditRows.Add(grv.RowIndex);
//get the record number (RecID)
recordnumber.Add(grv.Cells[2].Text.ToString());
}
}
else if (((CheckBox)grv.FindControl("TagRowChkBx")).Checked == false)
{
UserMessageLbl.Text = "CheckBox value = false!!!";
PopUpPnl.Visible = true;
}
else
{
UserMessageLbl.Text = "CheckBox value neither true or false = "+ (((CheckBox)grv.FindControl("TagRowChkBx")).ToString());
PopUpPnl.Visible = true;
}
I have an ASP table, with a dropdownlist and text box in its 1st row. I have a button on click of which a similar row should be added. I have the following function in its button click event.
public void addRow()
{
int flag = Table1.Rows.Count;
DropDownList ddl = new DropDownList();
ddl.ID = "ddl" + (flag + 1).ToString();
TextBox tBox = new TextBox();
tBox.ID = "txt" + (flag + 1).ToString();
TableCell tCell1 = new TableCell();
TableCell tCell2 = new TableCell();
tCell1.Controls.Add(ddl);
tCell2.Controls.Add(tBox);
TableRow tRow = new TableRow();
tRow.Cells.Add(tCell1);
tRow.Cells.Add(tCell2);
Table1.Rows.Add(tRow);
}
This code works fine when adding 2nd row. When I click the button again, the value of flag which should have the value of number of rows of the table is not getting incremented. So a new row doesn't get created. Can you please tell me why the value of flag doesn't get incremented ? Or is their an easier way to do this ? Direct answer or link would be helpful.
Mark up code for Table:
<asp:Table ID="Table1" runat="server" Width="410px">
<asp:TableRow>
<asp:TableCell>
<asp:DropDownList runat="server" ID="ddl1"></asp:DropDownList>
</asp:TableCell>
<asp:TableCell>
<asp:TextBox runat="server" ID="txt1"></asp:TextBox>
</asp:TableCell>
</asp:TableRow>
</asp:Table>
Warning - Big ugly block of text
I'm going to try and explain why what you're trying to do is not as simple as it sounds (or as simple as I'd like it to be) and hopfully give you an alterantive approach, which while it
may not be straight forward, it should be "better".
It looks a high level overview of what you are trying to do is the following:
Give the user the oppotunity to add one or more records to a collection (database) before finalising the save.
It is possible to do this using postback to generate the additional rows/items beofore clicking the final save button. Hover this may not be the easiest, or best, way to do this.
From a technical viewpoint adding dynamic controls has to be handeld in a very specific manner at a specific point in the ASP.net page lifecylce. Dynamic controls also have to be recreated on every post back, so every time a user clicks the "add" button, you will have to re-reate the rows you already have in addition to the new row. This is why you only get the one row in this example. Thats just the adding of the dynaimc controls. Wait till you get to getting data from your dynamic text boxes and drop downs!
From an end users perspective, this approach is also not ideal as every time the user clicks the "add" button the page cycles through the request/response process, which at the minumun cuases a bit of an ugly flash.
Update
But how? I hear you ask. Well here it is (I've simplified it to one column for this example):
ASPX
<asp:Table ID="Table1" runat="server">
<asp:TableRow>
<asp:TableCell><asp:TextBox ID="txt1" runat="server"> </asp:TextBox></asp:TableCell>
</asp:TableRow>
</asp:Table>
<asp:Button ID="btnAdd" runat="server" Text="Add" onclick="Button1_Click" />
<asp:HiddenField ID="hdnRowCount" runat="server" />
<asp:Button ID="btnSave" runat="server" onclick="btnSave_Click" Text="Save" />
<div>
<asp:Label ID="lblResult" runat="server" Visible="false"></asp:Label>
</div>
C#
private int rowCount = 1;
private bool rowNeeded = false;
protected override void OnPreInit(EventArgs e)
{
//Pre-emptively create additional row on post back.
//We'll remove it if we don't need it later
//Controls Have to be added at pre-init to maintain their view state
//Adding a row in the button click event will result in any data
//entered in that row dissapearing from viewstate on next post back
if (IsPostBack)
{
//Get number of additional rows from hidden field
//We're using a hidden form field and Request.Form
//instead of ViewsState and hdnRowCount.value
//because ViewState has not been loaded at this stage
//of the page life cycle
if (!string.IsNullOrEmpty(Request.Form["hdnRowCount"]))
{
rowCount = int.Parse(Request.Form["hdnRowCount"]);
}
for (int i = 0; i < rowCount; i++)
{
TableRow tr = new TableRow();
TableCell tc = new TableCell();
TextBox tb = new TextBox();
// +2 as there are existing controls with "1", eg txt1
tb.ID = string.Format("txt{0}",i+2);
tc.Controls.Add(tb);
tr.Cells.Add(tc);
Table1.Rows.Add(tr);
}
}
base.OnPreInit(e);
}
//Add Row Click
protected void Button1_Click(object sender, EventArgs e)
{
//Hold number of additional rows in hidne field
hdnRowCount.Value = (++rowCount).ToString();
//Let the page know we need to keep the row
rowNeeded = true;
}
//An Exmple of how to get your data out
protected void btnSave_Click(object sender, EventArgs e)
{
string s = "";
//Iterate through the table rows finding the controls
//Using count -1 as we still have the pre-emptive row
for(int i = 0; i < Table1.Rows.Count-1; i++)
{
TextBox tb = (TextBox)Table1.Rows[i].FindControl(string.Format("txt{0}",i+1));
s += "," + tb.Text;
}
lblResult.Text = s;
lblResult.Visible = true;
}
protected override void OnLoadComplete(EventArgs e)
{
//Get rid of pre-emptive row if we don't need it
if (IsPostBack)
{
if (!rowNeeded)
{
Table1.Rows.RemoveAt(Table1.Rows.Count - 1);
}
}
base.OnLoadComplete(e);
}
End Update
So, how would I do it? Client side, is the answer to that question. I would use jQuery to create a clone of the first row. You then would need to adjust the id and name attributes of your form fields before appending your new row to the table. You would also need a hidden form field to keep track of the number of hidden rows. When your user finaly hits the "save" button, on the C# side, get the number of additional rows from you hidden field the go old school and use Request.Form[] to get the value of each of your added fields.
This, in my opionion, will be a smoother experience for the end user.
Another, somewhat hacky option,if you have a defined maximum number of additional rows. For this have the mamimum rows, with form controls in the table. You can then either use visible="false" in the aspx and use post back to change that to visible="true" when the user clicks "add". The other option there is to give the row a CSS class that hides the row and then use javascript/jquery to remove the class to make the row visible.
The issue here is that you are not understanding the ASP.Net Page Lifecycle and more importantly how it interacts with dynamic controls (your added controls.)
Unfortunately, we can not answer this question without understanding whether the values in these "dynamically" created controls are used server-side or client-side.
I can guess that:
1) If they are for client side consumption/usage, you are doing wrong. If you need more info ask another (more specific) question.
2) If they are for server side consumption/usage, you must only allow one new record per postback, and should include a "Save" button that will save the current "new" row and cause a page refresh that the user can add a new entry to.
3) See rule #0 (Every rule has exceptions. {This rule applies in so far that you understand the problem at hand.})
Monk
I had posted a thread a while back about avoiding adding blank rows into the database. Before when something was to click add row it would automatically add a blank row into the database regardless if the user canceled the new row. I figured it out but now came to a new issue. If the user decides to reload the page(F5) while editing the new row, it will add a blank row into the database, thats a no no for my situation. Can anyone help me figure out how to avoid that?
Below is my insert code:
protected void gv_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Insert") //- this is needed to explain that the INSERT command will only work when INSERT is clicked
{
gv.DataBind();
DataTable d = dbcon.GetDataTable("SELECT * FROM CIS.CIS_TRANS ORDER BY ID DESC", "ProjectCISConnectionString");
string transCode = "", fundCode = "", BSA_CD = "", DP_TYPE = "";
if (d.Rows.Count > 0)
{
transCode = d.Rows[0]["TRANS_CD"].ToString();
fundCode = d.Rows[0]["FUND_CD"].ToString();
BSA_CD = d.Rows[0]["BSA_CD"].ToString();
DP_TYPE = d.Rows[0]["DP_TYPE"].ToString();
if (transCode.Trim().Length > 0)
{
dbcon.Execute("INSERT INTO CIS.CIS_TRANS (ID,TRANS_CD) VALUES(CIS.S_CIS_TRANS.nextval,'')", "ProjectCISConnectionString");
gv.DataBind();
}
}
gv.EditIndex = gv.Rows.Count - 1;
}
else if (e.CommandName == "Cancel")
{
DataTable d = dbcon.GetDataTable("SELECT * FROM CIS.CIS_TRANS ORDER BY ID DESC", "ProjectCISConnectionString");
string transCode = "";
if (d.Rows.Count > 0)
{
transCode = d.Rows[0]["TRANS_CD"].ToString();
if (transCode.Trim().Length == 0)
{
dbcon.Execute(string.Format("DELETE CIS.CIS_TRANS WHERE ID = '{0}'", d.Rows[0]["ID"]), "ProjectCISConnectionString");
gv.DataBind();
}
}
}
}
If you want to avoid inserting blank row by the user, you need validators. Add RequiredFieldValidator to the insert template:
<asp:RequiredFieldValidator runat="server" ID="valFieldName"
< ControlToValidate="txtFieldName" ErrorMessage="FieldName is missing" />
In your method check if Page.IsValid == true before you insert to the database.
EDIT:
If you want to avoid postbacks completely, you can insert the whole grid into an UpdatePanel:
<asp:UpdatePanel>
<ContentTemplate>
<asp:GrdiView>
....
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
Now when the user hits F5 he just reload the page again and not posting back an insert command.
It seems like you try to bring too much logic into one part of code. You put data base logic into button commands. Imagine the user just spams this button. That'd cause huge amounts of empty rows.
Try this approach: divide your database logic and your UI logic (e.g. MVVM). That's a common pattern, today.
Imagine you created all your objects and now the user hits the "save" button (that might also happen through changing a row - whatever). After the save command is executed your view model (the logic behind the view) could e.g. validate your data. So you could expand the functionality on your UI much easier (as said: validation or similar stuff).
If you'd like to do this with your current code, every expansion would blow it up more and more - and it doesn't take too much time till maintaining this code is horrendous.
After everything in your VM happend (validation etc.) the transaction command into your DB could happen (something like myEntities.Save()). And this method provides that your business objects get correctly transformed into SQL and stored in your database.
That was just a very brief explanation of the MVVM pattern, but that seems like the only proper solution here to me.
I've got a pretty strange problem with my usercontrols that I'm working with. I'm coding some user controls to be used in a DotNetNuke module.
Basically I have a UserControl on my page that holds some controls and then there is a Placeholder where I am loading in a UserControl that has a GridView placed in it.
Basically here is my page structure
<asp:Panel runat="server" ID="pnlServiceType" Visible="false">
<uc:ServiceType runat="server" ID="ServiceType" />
</asp:Panel>
Within that user control are some form elements and then the following:
<asp:PlaceHolder runat="server" ID="phServices" />
That placeholder then has a user control added to it like so:
<p class="header"><asp:Label runat="server" ID="lblServiceHeader" Font-Bold="true" /></p>
<asp:GridView runat="server" ID="gvServices" AlternatingRowStyle-BackColor="#eaeaea" BorderStyle="None" GridLines="None"
AutoGenerateColumns="false" CellPadding="6" Width="100%" EnableViewState="false" OnRowEditing="gvServices_RowEditing">
When the user picks something in the DropDownList, I'm clearing the controls out of the Placeholder, and re-adding just a single UserControl for the set of records for whatever type they've picked like so
_serviceID = e.Value;
//Clear the controls out of the placeholder
phServices.Controls.Clear();
//Reset the count of grids for the OnInit() method
Session["GridCount"] = 0;
if (e.Value != "NULL")
{
PopulateServicesGrid(_service.GetServicesByFormType(Convert.ToInt32(e.Value)));
}
else
{
PopulateServicesGrid(_service.GetServicesByClient(Convert.ToInt32(_client)));
}
And the PopulateServicesGrid method:
private void PopulateServicesGrid(List<NOAService> services)
{
//Creates a LINQ grouping based on the Billing Codes
//Allows a super easy creation of grids based on the grouped billing codes
var query = services.Select(service => service.BillingCode).Distinct();
foreach (string code in query)
{
var servicesByCode = services.Where(service => service.BillingCode == code).ToList();
ServicesGrid servicesGrid = LoadControl("~/DesktopModules/LEL Modules/NOA/ServicesGrid.ascx") as ServicesGrid;
Label lblServiceHeader = servicesGrid.FindControl("lblServiceHeader") as Label;
GridView gvServices = servicesGrid.FindControl("gvServices") as GridView;
phServices.Controls.Add(servicesGrid);
servicesGrid.ID = code;
lblServiceHeader.Text = servicesByCode[0].FormTypeName;
gvServices.DataSource = servicesByCode;
gvServices.DataBind();
Session["GridCount"] = phServices.Controls.Count;
}
}
And on my ServiceType UserControl, on the PageInit, I'm readding the ServiceGrid usercontrol so that my grids show up across postbacks and aren't lost from the Placeholder
void NOAServiceType_Init(object sender, EventArgs e)
{
for (int i = 0; i < Convert.ToInt32(Session["GridCount"]); i++)
{
ServicesGrid servicesGrid = LoadControl("~/DesktopModules/LEL Modules/NOA/ServicesGrid.ascx") as ServicesGrid;
phServices.Controls.Add(servicesGrid);
}
}
The grids populate successfully and everything seems to work just fine. But for some reason, on my GridView, I have a CommandField of
<asp:CommandField ShowEditButton="true" ItemStyle-HorizontalAlign="Right" EditText="Edit" UpdateText="Update"
EditImageUrl="~/images/LELModules/appbar.edit.rest.png" CancelImageUrl="~/images/LELModules/appbar.close.rest.png" UpdateImageUrl="~/images/LELModules/appbar.check.rest.png"
ButtonType="Image" CausesValidation="false" />
When I click my Edit command on the Grid row, nothing happens. My grid doesn't lose its rows, the control is still there, everything seems like it should be ok. The RowEditing event doesn't fire until I click it a second time.
Any idea why this might be occuring?
UPDATE: I've managed to figure out that my SelectedIndexChanged handlers are effectively resetting the DataSource on the Grid contained by the UserControl when they are readded to the PlaceHolder. When the CommandField (Edit) is clicked though, the Init fires for the UserControl that holds the placeholder
ServiceType UserControl < `Init` fires here
-Form elements
-Placeholder which holds
--UserControl with GridView
The Init method loads up new instances of the UserControl and adds them to the PlaceHolder, but the DataSource is null. With EnableViewState=true it looks like the data is still bound, but if I handle PreRender, I can see that the DataSource on my gvServices | null
Is it even possible to edit rows like this on a GridView that is being added dynamically to a PlaceHolder over and over?
FIXED I found out what the issue was after referring to this article
http://www.west-wind.com/weblog/posts/2006/Feb/24/Overriding-ClientID-and-UniqueID-on-ASPNET-controls
It got me thinking, what if the IDs were getting changed? The controls are way nested. So I went and put a watch on the GridView's ID, ClientID, and UniqueID just to see. When the Control is loaded on my Init handler, it's assigned a super generic ID when it's added.
private void PopulateServicesGrid(List<NOAService> services)
{
//Creates a LINQ grouping based on the Billing Codes
//Allows a super easy creation of grids based on the grouped billing codes
var query = services.Select(service => service.BillingCode).Distinct();
foreach (string code in query)
{
var servicesByCode = services.Where(service => service.BillingCode == code).ToList();
ServicesGrid servicesGrid = LoadControl("~/DesktopModules/LEL Modules/NOA/ServicesGrid.ascx") as ServicesGrid;
Label lblServiceHeader = servicesGrid.FindControl("lblServiceHeader") as Label;
GridView gvServices = servicesGrid.FindControl("gvServices") as GridView;
phServices.Controls.Add(servicesGrid);
**servicesGrid.ID = code;**
lblServiceHeader.Text = servicesByCode[0].FormTypeName;
gvServices.DataSource = servicesByCode;
gvServices.DataBind();
Session["GridCount"] = phServices.Controls.Count;
}
}
I was setting the ID as something else. So when I was hitting the Edit RowCommand on my grid, it was reloading the controls again on Init and the IDs were being changed back from my custom set code (T2020, pulled from my database) to the generic ID again, it didn't know how to fire the event.
I hope this helps someone as I've lost at least 12 hours fixing this problem.
I have a gridView table (using ASP.NET 3.5) that is populated using a SqlDataSource object (from my SQL DB), that changes dynamically from different SELECT Command, according to different Authorization rules (more/fewer information is retrieved).
In GridView I have set AllowSorting=true and it's working perfectly.
my problem is:
I want to disable Sorting for specific Columns (E.g. Photo), and I understand it's done by setting:
myGrid.Columns[i].SortExpression = "";
Only, when I'm trying to access this specific column, I'm getting the error of:
`Index Out Of Bound (column doesn't exist yet!).
I tried connecting to the following events:
RowDataBound
RowataBinding
and it still did not work.
How can I access the specific column (once it's dynamically created) and disable it's sorting?
Here is Snippet:
ASPX Page:
<asp:GridView ID="gridNew" runat="server" AllowPaging="True"
AllowSorting="True" EnableModelValidation="True">
.CS Page:
protected void btnSend_Click(object sender, EventArgs e)
{
gridNew.DataSourceID = "Employees_DataSource";
switch (ddlSelection.SelectedValue)
{
case "Admin":
Employees_DataSource.SelectCommand = #"SELECT * FROM [Employees]";
gridNew.AutoGenerateDeleteButton = true;
gridNew.AutoGenerateEditButton = true;
gridNew.DataBind();
//gridNew.Columns[1].SortExpression = "";
// This is not working!! :(
break;
case "Manager": ...
default:
break;
}
Thanks! :)
Mitsy.
You can disable sorting on any column (including dynamic) by disabling the cell on the GridView DataBound event. This will give the desired result of disabling the column's header from appearing as a button.
protected void yourgrid_DataBound(object sender, EventArgs e)
{
yourgrid.HeaderRow.Cells[4].Enabled = false;
yourgrid.HeaderRow.Cells[5].Enabled = false;
}
You should handle the GridView.Sorting event. The GridViewSortingEventArgs.SortExpression is writable.
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
// replace with your logic
String newOrder = "Column1";
e.SortExpression = newOrder;
e.SortDirection = "DESC";
}
Edit: If you just want to disable sorting for a specific column in an event, normally it should work to set the GridViewColumn's SortExpression to null:
GridView1.Columns[0].SortExpression = null;
However, you are using a SqlDataSource(which i'm not familiar with and personally avoid) and you've created your GridView dynamically. Is also the GridView's RowCollection empty? Are you recreating your dynamic GridView(s) on every postback with the same ID as before in Page_Load at the latest?