Reputation: 1528
I have the following SQL
SELECT Equipment.* ,cast(EquipmentId as varchar(100)) + ' ' + EquipmentName as Name
FROM Equipment
WHERE Equipment.EquipmentCategoryID = @EquipmentCategoryID
AND (Equipment.EquipmentSubCategoryID = @EquipmentSubCategoryID
OR (@EquipmentSubCategoryID is null OR @EquipmentSubCategoryID = '' ))
In SQL Server it is behaving as expected. When @EquipmentCategoryId
is 12 and @EquipmentSubCategoryID
is null It returns all the values I want. And when @EquipmentCategoryId
is 12 and @EquipmentSubCategoryID
is another number it returns the smaller amount of rows which is also what I want.
In an ASP.NET dropdownlist in a GridView it is not behaving as expected, returning all the rows for each dropdownlist
in the GridView
, even though the @EquipmentSubCategoryID
has different numbers, that work in SQL Server. The dropdownlist
is bound like this.
protected void ServiceFormsGV_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
HiddenField EquipmentCategoryIDHF = (HiddenField) e.Row.FindControl("EquipmentCategoryIDHF");
HiddenField EquipmentSubCategoryIDHF = (HiddenField) e.Row.FindControl("EquipmentSubCategoryIDHF");
DropDownList EquipmentDD = (DropDownList) e.Row.FindControl("EquipmentDD");
EquipmentDS.SelectParameters["EquipmentCategoryID"].DefaultValue = EquipmentCategoryIDHF.Value;
EquipmentDS.SelectParameters["EquipmentSubCategoryID"].DefaultValue = EquipmentSubCategoryIDHF.Value;
EquipmentDD.Items.Clear();
EquipmentDD.Items.Add(new ListItem());
EquipmentDD.DataBind();
}
}
I can confirm that each time through the method that the HiddenFields are displaying the correct values but the ServiceFormsGV
GridView
has all the rows in each DropDownList
.
The GridView is as follows.
<asp:GridView ID="ServiceFormsGV" runat="server" AutoGenerateColumns="False" DataKeyNames="ServiceFormID,EquipmentCategoryID1" DataSourceID="ServiceFormsDS" OnDataBound="ServiceFormsGV_DataBound" OnRowDataBound="ServiceFormsGV_RowDataBound">
<Columns>
<asp:TemplateField HeaderText="Machine Id">
<ItemTemplate>
<asp:DropDownList ID="EquipmentDD" runat="server" AutoPostBack="True" DataSourceID="EquipmentDS" DataTextField="EquipmentName" AppendDataBoundItems="True"
DataValueField="EquipmentID" OnSelectedIndexChanged="EquipmentDD_SelectedIndexChanged">
And in the SQLDataSource I have
CancelSelectOnNullParameter="False"
which is necessary.
I have tested the query in Configure Data Source and it behaves as expected.
Upvotes: 0
Views: 161
Reputation: 6891
The reason behind this behavior is SqlDataSource
being used as DataSource for the dropdownlists inside the GridView.
During DataGridView_RowDataBound event you are changing default values of the parameters of SqlDataSource
and rebinding the dropdownlist of the row. When you change the Default values of parameters the outcome of the query execution by SqlDataSource
impacts all the dropdownlists so eventually all the dropdownlists will have the query result generated by the values passed by the last row of the gridview. That is why you get the same result in all the dropdownlists of the grid.
You need to run the query everytime and retrieve the result and bind it separately to each dropdownlist without using any common datasource among them.
While the solution you found is works with no issues, it would require a SQL connection to be opened and run query against the db for each Row of DataGridView.
The approach I suggest is to retrieve the all the rows from the Equipment table and store them as a collection of entities at page level and then during rowDataBound event filter the collection for the current Category and SubCategory and bind the filtered result to the dropdown list.
Following is the entity Equipment
public class Equipment
{
public int Id { get; set; }
public string Name { get; set; }
public string CategoryId { get; set; }
public string SubCategoryId { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", Id, Name);
}
}
}
In the Page_load populate the collection of Equipment by retrieving all the rows from Equipment table in the database.
public partial class _Default : Page
{
private List<Equipment> equipments;
protected void Page_Load(object sender, EventArgs e)
{
equipments = new List<Equipment>();
var commandText = "SELECT EquipmentId, EquipmentName, CategoryId, SubCategoryId FROM Equipment";
using (var connection = new SqlConnection("Data Source=.;Initial Catalog=LocalDevDb;User id=sa;Password=Password1"))
{
using (var command = new SqlCommand(commandText, connection))
{
command.CommandType = System.Data.CommandType.Text;
connection.Open();
using (var reader = command.ExecuteReader(System.Data.CommandBehavior.CloseConnection))
{
while (reader.Read())
{
var equipment = new Equipment
{
Id = reader.GetInt32(0),
Name = reader.GetString(1),
CategoryId = reader.GetString(2),
SubCategoryId = reader.GetString(3),
};
equipments.Add(equipment);
}
}
}
}
}
}
Filter this collection based on the CategoryId and SubCategoryId and bind the result to the DropDown.
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DropDownList EquipmentDD = (DropDownList)e.Row.FindControl("dropDownList");
HiddenField EquipmentCategoryIDHF = (HiddenField) e.Row.FindControl("EquipmentCategoryIDHF");
HiddenField EquipmentSubCategoryIDHF = (HiddenField) e.Row.FindControl("EquipmentSubCategoryIDHF");
var categoryId = EquipmentCategoryIDHF.Value;
var subCategoryId = EquipmentSubCategoryIDHF.Value;
Func<Equipment, bool> criteria;
if(!string.IsNullOrEmpty(subCategoryId))
{
criteria = criteria = equip => equip.CategoryId == categoryId && equip.SubCategoryId == subCategoryId;
}
else
{
criteria = equip => equip.CategoryId == categoryId;
}
var list = equipments.Where(criteria).ToList();
EquipmentDD.DataSource = list;
EquipmentDD.DataBind();
EquipmentDD.Items.Insert(0, new ListItem());
}
}
Here in my example, CategoryId and SubCategoryId columns are created with String datatype. You need to change them to appropriate if they are different than columns of your table.
This should help you solve your issue.
Upvotes: 1
Reputation: 1528
The solution I have found is that the SqlDataSource
needs to to be in the GridView
for the row. So in the same place as the DropDownList
in the ItemTemplate
. That wasy the SqlDataSource is updated for each row in the RowDataBound method.
protected void ServiceFormsGV_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
HiddenField EquipmentCategoryIDHF = (HiddenField) e.Row.FindControl("EquipmentCategoryIDHF");
HiddenField EquipmentSubCategoryIDHF = (HiddenField) e.Row.FindControl("EquipmentSubCategoryIDHF");
SqlDataSource EquipmentDS = (SqlDataSource)e.Row.FindControl("EquipmentDS");
DropDownList EquipmentDD = (DropDownList) e.Row.FindControl("EquipmentDD");
EquipmentDS.SelectParameters["EquipmentCategoryID"].DefaultValue = EquipmentCategoryIDHF.Value;
EquipmentDS.SelectParameters["EquipmentSubCategoryID"].DefaultValue = EquipmentSubCategoryIDHF.Value;
EquipmentDD.Items.Clear();
EquipmentDD.Items.Add(new ListItem());
EquipmentDD.DataBind();
}
}
<asp:GridView ID="ServiceFormsGV" runat="server" AutoGenerateColumns="False" DataKeyNames="ServiceFormID,EquipmentCategoryID1" DataSourceID="ServiceFormsDS" OnDataBound="ServiceFormsGV_DataBound" OnRowDataBound="ServiceFormsGV_RowDataBound">
<Columns>
<asp:TemplateField HeaderText="Machine Id">
<ItemTemplate>
<asp:DropDownList ID="EquipmentDD" runat="server" AutoPostBack="True" DataSourceID="EquipmentDS"
DataTextField="EquipmentName" AppendDataBoundItems="True"
DataValueField="EquipmentID" OnSelectedIndexChanged="EquipmentDD_SelectedIndexChanged">
<asp:SqlDataSource ID="EquipmentDS" runat="server" CancelSelectOnNullParameter="False" ConnectionString="<%$ ConnectionStrings:XXXXXXXXXXXXXXXXX %>" SelectCommand="SELECT Equipment.* ,cast(EquipmentId as varchar(100)) + ' ' + EquipmentName as Name FROM Equipment WHERE Equipment.EquipmentCategoryID = @EquipmentCategoryID AND (Equipment.EquipmentSubCategoryID = @EquipmentSubCategoryID OR (@EquipmentSubCategoryID is null OR @EquipmentSubCategoryID = '' ) )">
<SelectParameters>
<asp:Parameter Name="EquipmentCategoryID" />
<asp:Parameter Name="EquipmentSubCategoryID" />
</SelectParameters>
</asp:SqlDataSource>
</ItemTemplate>
It makes sense and it works.
Upvotes: 0