GridView Data Bound中的下拉列表表现不正确ASP.NET C#

时间:2017-05-26 01:31:31

标签: c# asp.net sql-server gridview

我有以下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 = '' ))

在SQL Server中,它的行为符合预期。当@EquipmentCategoryId为12且@EquipmentSubCategoryID为null时,它返回我想要的所有值。当@EquipmentCategoryId为12而@EquipmentSubCategoryID是另一个数字时,它会返回较小的行数,这也是我想要的。

在GridView的ASP.NET下拉列表中,它的行为不符合预期,返回dropdownlist中每个GridView的所有行,即使@EquipmentSubCategoryID具有不同的数字,在SQL Server中工作。 dropdownlist就是这样绑定的。

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();  
    }
}  

我可以确认每次通过HiddenFields显示正确值的方法,但ServiceFormsGV GridView包含每个DropDownList中的所有行。 GridView如下。

<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">

在SQLDataSource中我有

CancelSelectOnNullParameter="False"

这是必要的。

我在配置数据源中测试了查询,它的行为符合预期。

Testing in ASP.NET SqlDataSource

2 个答案:

答案 0 :(得分:1)

这种行为背后的原因是SqlDataSource被用作GridView内部下拉列表的DataSource。

在DataGridView_RowDataBound事件期间,您将更改SqlDataSource参数的默认值并重新绑定行的下拉列表。当您更改参数的默认值时,SqlDataSource执行查询的结果会影响所有下拉列表,因此最终所有下拉列表都将具有由gridview的最后一行传递的值生成的查询结果。这就是为什么你在网格的所有下拉列表中得到相同的结果。

您需要每次都运行查询并检索结果并将其单独绑定到每个下拉列表,而不使用其中的任何公共数据源。

虽然您找到的解决方案没有问题,但它需要打开SQL连接并针对DataGridView的每一行对数据库运行查询。

我建议的方法是从Equipment表中检索所有行,并将它们存储为页面级别的实体集合,然后在rowDataBound事件期间过滤当前Category和SubCategory的集合,并将过滤后的结果绑定到下拉列表。

以下是实体设备

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);
        }
    }
}

在Page_load中,通过从数据库中的Equipment表中检索所有行来填充设备集合。

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);
                    }
                }
            }
        }
    }
}

根据CategoryId和SubCategoryId过滤此集合,并将结果绑定到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());

    }
}

在我的示例中,使用String数据类型创建了CategoryId和SubCategoryId列。如果它们与表的列不同,则需要将它们更改为适当的。

这可以帮助您解决问题。

答案 1 :(得分:0)

我找到的解决方案是SqlDataSource需要在行的GridView中。因此,与DropDownList中的ItemTemplate位于同一位置。那就是为RowDataBound方法中的每一行更新SqlDataSource。

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>

这很有道理并且有效。