I have 3 nested DataLists data being loaded from SQL query for each. DataList1_ItemCommand is working properly but DataList2_ItemCommand is not even firing.
protected void DataList1_ItemCommand(Object source, DataListCommandEventArgs e)
{
if (e.CommandName == "Obiectiv")
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
int idob = Convert.ToInt32(DataList1.DataKeys[e.Item.ItemIndex].ToString());
DataList firstDataList = e.Item.FindControl("DataList2") as DataList;
List<SqlParameter> prm = new List<SqlParameter>();
prm.Add(new SqlParameter("@IdOB", idob));
firstDataList.DataSource = GetDataTable("SELECT DISTINCT IdCL,Cladire FROM tblCladire WHERE IdOB= @IdOB;", prm);
firstDataList.DataBind();
HtmlControl theDivS = (HtmlControl)e.Item.FindControl("DivS");
theDivS.Attributes["class"] = theDivS.Attributes["class"].Replace("hidden", "").Trim();
}
}
}
protected void DataList2_ItemCommand(Object source, DataListCommandEventArgs e)
{
if (e.CommandName == "Cladire")
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
DataList secondDataList = e.Item.FindControl("DataList2") as DataList;
int idcl = Convert.ToInt32(secondDataList.DataKeys[e.Item.ItemIndex].ToString());
DataList thirdDataList = e.Item.FindControl("DataList3") as DataList;
List<SqlParameter> prm = new List<SqlParameter>
{
new SqlParameter("@IdCL", idcl)
};
thirdDataList.DataSource = GetDataTable("SELECT DISTINCT IdCA,Camera FROM tblCamera WHERE IdCL =@IdCL;", prm);
thirdDataList.DataBind();
HtmlControl theDivT = (HtmlControl)e.Item.FindControl("DivT");
theDivT.Attributes["class"] = theDivT.Attributes["class"].Replace("hidden", "").Trim();
}
}
}
private static DataTable GetDataTable(string query, IEnumerable<SqlParameter> prms = null)
{
string constr = ConfigurationManager.ConnectionStrings["ConnString"].ConnectionString;
using (SqlConnection conn = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = query;
using (SqlDataAdapter sda = new SqlDataAdapter())
{
cmd.Connection = conn;
sda.SelectCommand = cmd;
using (DataSet ds = new DataSet())
{
DataTable dt = new DataTable();
if (prms is object)
{
foreach (var prm in prms)
{
cmd.Parameters.Add(prm);
}
}
sda.Fill(dt);
return dt;
}
}
}
}
}
First set of data is loaded on Page Load and it’s working fine. Then, for each value in the list on click (command) load second set of data in DataList1 then on click (command) to load third set of data. What I have so far is first and second set of data. CommandName="Cladire" is not trigered.
<div id="DivP" runat="server">
<div class="row no-gutters">
<asp:DataList ID="DataList1" runat="server" Style="width: 100%" OnItemCommand="DataList1_ItemCommand" DataKeyField="IdOB">
<ItemTemplate>
<asp:TextBox ID="txtIDOB" class="hidden" runat="server" Text='<%#DataBinder.Eval(Container.DataItem,"IdOB")%>' />
<asp:LinkButton ID="LinkButton10" runat="server" Text='<%#Eval("Obiectiv")%>' CommandName="Obiectiv" UseSubmitBehavior="false" Style="font-size: 11px;background-color:lightgreen" class="form-control form-control-sm" ClientIDMode="Static" />
<div id="DivS" runat="server" class="hidden">
<hr style="border: 1px solid #800080" />
<asp:DataList ID="DataList2" runat="server" Style="width: 100%" OnItemCommand="DataList2_ItemCommand" DataKeyField="IdCL">
<ItemTemplate>
<asp:TextBox ID="txtIDCL" class="hidden" runat="server" Text='<%#DataBinder.Eval(Container.DataItem,"IdCL")%>' />
<asp:LinkButton ID="LinkButton11" runat="server" Text='<%#Eval("Cladire")%>' CommandName="Cladire" UseSubmitBehavior="false" Style="font-size: 11px;background-color:lightsalmon" class="form-control form-control-sm" ClientIDMode="Static" />
<div id="DivT" runat="server" class="hidden">
<hr style="border: 1px solid #800080" />
<asp:DataList ID="DataList3" runat="server" Style="width: 100%" OnItemCommand="DataList3_ItemCommand" DataKeyField="IdCA">
<ItemTemplate>
<asp:TextBox ID="txtIDCA" class="hidden" runat="server" Text='<%#DataBinder.Eval(Container.DataItem,"IdCA")%>' />
<asp:LinkButton ID="LinkButton11" runat="server" Text='<%#Eval("Camera")%>' CommandName="Camera" UseSubmitBehavior="false" Style="font-size: 11px;background-color:lightblue" class="form-control form-control-sm" />
</ItemTemplate>
</asp:DataList>
<hr style="border: 1px solid #800080" />
</div>
</ItemTemplate>
</asp:DataList>
<hr style="border: 1px solid #800080" />
</div>
</ItemTemplate>
</asp:DataList>
</div>
</div>
2
Answers
How are you confirming the command is not triggered? Because, while that is possible, there is a separate issue in the code that will prevent you from seeing data even if the code does run.
In the second method, the
idcl
variable is aTextbox
rather than astring
. The result is the SQL command will always look like this, no matter what value someone enters in the textbox:Because it will implicitly call
ToString()
on the TextBox object, and most objects just output the type name if not explicitly changed to do something else. I doubt there’s any data in your database matching that string value.Of course, the quick/naïve solution is using the control’s
.Text
property to get the desired string value for the SQL statement, instead of the control itself.But that would be really bad.
The real solution is understanding the
GetDataTable()
method is fundamentally flawed in a way that is forcing you to write seriously unsafe code. You need to look into using parameterized queries, so the code will look something more like this:Here we see I’m imagining a revision to the
GetDataTable()
method that allows to pass anIEnumerable<SqlParameter>
as an additional argument. This isn’t the only solution or way to accomplish it, but the important thing is you should NEVER use string concatenation to substitute data into an SQL statement!!When you have this part working, you need to do same thing for the
idob
variable in the first method. Fixing this throughout your application should be your top priority.Again: it is also possible the code is indeed not even running, and I have another idea about why that may be (related to the page life cycle and needing to re-build the outer control in the
Pre-Init
phase, so events for the nested inner controls can be restored at the right time). But I won’t get into that more until we’ve looked at this other issue, which is a HUGE gaping security problem.Separate from the bad TextBox + SQL injection issue, there is a problem with how this code uses the ASP.Net Webforms Page Lifecycle.
Every button click is a complete rebuild of the entire page from scratch, including the base grid. If not done correctly, at the point in the page life cycle where ASP.Net should be registering the click event to fire, the grid elements will not exist yet. If that happens, the event won’t fire.
Personally, I think they should make a new exception type specifically for this issue that is thrown when it’s time to wire up an event to a control that doesn’t exist, with an exception message the explains what’s going on.
To resolve it, you have two options:
Make sure your parent grid is re-populated from the database before the events are registered. This means building the grid in the
PreInit
event instead of theLoad
event.If the main grid itself loads or updates in response to an event — perhaps something like a textbox with a search filter — that also means ensuring that state is saved in a mechanism available at PreInit time.
Refactor to use an
UpdatePanel
managed from javascript, instead of a nested grid. This can dramatically improve both real and user-perceived performance, because with an UpdatePanel the page in the browser does not unload (no DOM freeze/rebuild) and you only need to query the database for the child data; you don’t need to rebuild the rest of the page on the server.Either way, it’s a significant change from the original post. This scenario is one of the reasons webforms has fallen out of favor.