【问题标题】:Dynamically created Items in DropDownList in GridView not working在 GridView 中的 DropDownList 中动态创建的项目不起作用
【发布时间】:2021-08-31 19:37:23
【问题描述】:

我在.aspx 页面上有一个.ascx 用户控件。当用户点击一个按钮时,信息被收集并存储在数据库中。然后Gridview 是数据绑定的,Gridviewdropdownlist。这个dropdownlistItems 是根据用户之前的输入动态创建的,现在在数据库中。这一切都很好,Gridviewdropdownlist 一起显示,并带有动态创建的 Items

问题在于这个dropdownlist 的回发。我显然必须在dropdownlist 中使用动态创建的Items 重新创建Gridview。这不起作用。回发发生并调用Page_Init,然后调用Page_InitComplete。这在Gridview 上有数据绑定,它调用SectionGV_OnRowDataBound 方法。 dropdownlists 被重新创建。但是SectionDD_OnSelectedIndexChanged 方法永远不会被命中,然后dropdownlist 只是恢复到原来的值。我无法更改 dropdownlist's 选择的值。

.ASCX

<asp:GridView ID="MyGV" runat="server" AutoGenerateColumns="False" DataSourceID="MyDS" Width="100%" OnRowDataBound="MyGV_OnRowDataBound">
    <Columns>                                   
        <asp:TemplateField >
             <ItemTemplate>
                   <asp:DropDownList runat="server" ID="SectionDD" AppendDataBoundItems="True" AutoPostBack="True" OnSelectedIndexChanged="SectionDD_OnSelectedIndexChanged" >
                   </asp:DropDownList>
             </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

.ASCX 背后的代码

protected void Page_Init(object sender,EventArgs e)
{                                                                
    this.Page.InitComplete += Page_InitComplete;
}
private void Page_InitComplete(object sender, EventArgs e)
{
    MyGV.DataBind();
}
protected void SectionDD_OnSelectedIndexChanged(object sender, EventArgs e)
{
        
}
protected void MyGV_OnRowDataBound(object sender, GridViewRowEventArgs e)
{
    using (EntitiesModel dbContext = new EntitiesModel())
    {
        // get array[][] array from database                
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            DropDownList sectionDd = (DropDownList)e.Row.FindControl("SectionDD");
            sectionDd.Items.Clear();
                
            if (array.Length == 3)
            {
                if(array[0][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[0][0], array[0][1]));
                if(array[1][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[1][0], array[1][1]));
                if(array[2][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[2][0], array[2][1]));
            }
            else if (array.Length == 2)
            {
                if (array[0][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[0][0], array[0][1]));
                if (array[1][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[1][0], array[1][1]));
            }
            else if (array.Length == 1)
            {
                if (array[0][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[0][0], array[0][1]));
                }
            }                    
            sectionDd.DataBind();
            }
        }
    }
}
private void SaveToDB()
{
    //save information to database

注意

这是我尝试解决此问题的众多方法之一。我将信息保存在数据库中的原因只是临时修复。我只是想解决上面列出的问题,然后我将添加一个ViewState 解决方案。

【问题讨论】:

    标签: c# asp.net gridview dynamic


    【解决方案1】:

    首先,不,您不必重新创建或重新加载网格,或者回帖中的下拉列表。

    用户可以在网格中输入 - 更改值,选择下拉菜单。此时,您可以循环所有网格行并获取/抓取选定的下拉值。

    您当然可以触发/触发下拉事件,但完全不清楚您是否真的需要该事件。

    如果您的网格在回发时弄乱了?那么这意味着您没有在第一个页面加载时限制加载 - 之后,这应该无关紧要。

    假设我们有这个网格标记:

        <asp:GridView ID="MyGrid" runat="server" CssClass="table table-hover" 
              DataKeyNames="ID" AutoGenerateColumns="false" OnRowDataBound="MyGrid_RowDataBound" >
    
            <Columns>
               <asp:BoundField DataField="FirstName" HeaderText="FirstName"  />
               <asp:BoundField DataField="LastName" HeaderText="Last Name" />
               <asp:BoundField DataField="HotelName" HeaderText="Hotel Name" />
    
                <asp:TemplateField HeaderText="City">
                    <ItemTemplate>
                        <asp:DropDownList ID="DropDownList1" runat="server" 
                            DataTextField="City" 
                            DataValueField="City"
                            >
                        </asp:DropDownList>
                    </ItemTemplate>
                </asp:TemplateField>
    
               <asp:BoundField DataField="Province" HeaderText="Province" />
    
            </Columns>
        
          </asp:GridView>
    

    当然还有非模板字段(例如前几个 boundField - 它们出现在 .Cells 集合中。但对于模板列,您使用 findcontrol。

    但是,在每个和几乎所有网页中,作为一般规则,您只加载 + 混乱 + 创建一次网格,并且您只在第一页加载时执行此操作 - 故事结束!如果你不遵守这条规则,那么你就处于一个受伤的世界。

    好的,让我们加载网格。因为我们有一个下拉列表,所以我们必须在绑定的项目数据上填写它。

    所以,我们的代码将如下所示:

       public DataTable rstCity = new DataTable();
        protected void Page_Load(object sender, EventArgs e)
        {
            if (IsPostBack == false)
            {
                LoadGrid();
            }
        }
    
        public void LoadGrid()
        {
            using (SqlCommand cmdSQL = new SqlCommand("SELECT City from City Order by City",
                new SqlConnection(Properties.Settings.Default.TEST4)))
            {
                // locd city for drop down list
                cmdSQL.Connection.Open();
                rstCity.Load(cmdSQL.ExecuteReader());
    
                // now load grid
                cmdSQL.CommandText = "SELECT * from tblHotels ORDER BY HotelName";
                DataTable rst = new DataTable();
                rst.Load(cmdSQL.ExecuteReader());
                MyGrid.DataSource = rst;
                MyGrid.DataBind();
            }
        }
    

    请注意我如何将 city 表的范围限定为类级别(没有理由为每一行反复加载它 - 在第一页加载后,它会超出范围 - 我们不在乎 - 它会在第一页加载和数据绑定期间生效。

    好的,现在,让我们为网格进行项目数据绑定。

    我们有这个:

      protected void MyGrid_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                DropDownList MyDrop = (DropDownList)e.Row.FindControl("DropDownList1");
                MyDrop.DataSource = rstCity;
                MyDrop.DataBind();
                // get City from current data row source - set the drop to data row City
                MyDrop.SelectedValue = ((DataRowView)e.Row.DataItem)["City"].ToString();
            }
        }
    

    好的,就是这样。 (请注意“数据项”如何仅在数据绑定期间存在 - 一个方便的提示,从那时起您就可以使用 FULL 数据行 - 包括 PK 值,以及您甚至不包括在网格标记中的列。但是,一旦数据绑定结束,然后 DataItem 不能使用 - 它只在绑定过程中存在。但是这个信息非常有价值,因为你已经充分利用了你用来绑定的实际数据源。在上面,我需要 City from设置下拉列表的那一行。

    好的,现在的输出是这样的:

    好的,因为我们只在第一页加载时绑定。此时,该网格的视图状态 100% 由 asp.net 自动处理。

    您可以在此表单上删除其他按钮 - 回发应该无关紧要。这里的重要教训是 gridview 确实会持续存在(至少如果我们不每次都在回发时重新加载它会持续存在 - 你不应该重新加载)。

    好的,下一期:

    在大多数情况下,我看不到网格中需要下拉列表事件?

    因此,作为一般规则,您可以选择和更改任何行组合。完成后,您可以通过循环数据网格行来获取每一行的值 - 包括选择的下拉列表。

    但是,无论如何,让我们连接下拉列表事件。

    每日提示: 既然我们不能选择下拉菜单并使用属性表?

    然后在标记中这样做:

    您可以在标记类型中的 OnSelectedIndexChanged=

    当您点击“=”符号时,请注意非常接近 intel-sense 如何弹出事件创建选项:

    所以,点击 CreateNewEvent - 它“似乎”没有发生任何事情,但事实上,如果你翻到后面的代码,你已经创建了一个不错的事件存根。

    所以让我们把我们的代码放在那个事件中 - 抓取行 - 显示刚刚选择的值。

        protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
        {
            // user changed the combo box
            DropDownList MyDrop = (DropDownList)sender;
    
            GridViewRow gRow = (GridViewRow)MyDrop.Parent.Parent;
    
            Response.Write("<h2>Row index is = " + gRow.RowIndex.ToString() + "</h2>");
            Response.Write("<h2>Hotel Name is = " + gRow.Cells[2].Text + "</h2>");
            Response.Write("<h2>City from Drop selected = " + MyDrop.SelectedValue + "</h2>");
    
    
        }
    

    当然,我们在 drop 标记中也设置了 auto post back = true - 对吧?

    并注意我们如何获取“发送者”,然后使用 .Parent.Parent 获取我们正在操作的网格行。第一个父级是某个单元格或类似的单元格。

    我实际上构建了递归函数来获取该值,但在这里我们只使用了 .parent.Parent(通常带有额外的标记,然后您需要再操作一个 .parent)。

    不管怎样,现在说我选择了上面的最后一个下拉菜单,然后更改它。

    我得到这个输出:

    总结:

    只有永远加载网格 - 第一页加载 - (你检查 IsPostBack)。

    您可以拥有各种额外的回帖 - 网格应该可以存活并且很好 - 根本不需要重新加载。 (GridView 已内置 viewstate)。

    现在,在我玩得开心之后,更改多行的下拉菜单?

    我可以循环网格视图 - 对该网格的任何更改都将持续存在。

    事实上,如果您将列设为文本框(在模板化字段中),那么您几乎可以像 Excel 一样使用 Tab 键进行编辑和编辑,并且这些值将再次为您保留,并且它们在回发后仍然存在。 (然后您可以在一个更新命令中将整个更改网格发送回数据库 - 我可以展示如何执行此操作,但无论如何这篇文章已经有很多好东西了。

    现在,说了以上,做了以上?

    既然我们已经完成了这项工作,那么我们可以回去构建一个自定义用户控件 - 但如果做得好,它也应该表现正确,并且还应该能够在回发中存活。

    【讨论】:

    • 我遗漏了一些东西。我理解你的理论,但永远不会点击 onchange 方法,并且下拉列表的选定值只会恢复。
    • 我已经解决了这个问题。在设置下拉列表项的值时,它们具有相同的值。所以 onselectedindexchange 方法没有检测到变化,所以没有触发。这导致我得出错误的结论,即我需要重新创建下拉列表,因为它是动态创建的。正如你向我展示的那样,这是不正确的。谢谢。你带领我走上了通往真理的道路。我也应该知道得更好。我有句话。当我在互联网上找不到类似的问题时,我正在做一些非常愚蠢的事情。
    • 对于下拉列表?它只会在您 autopostback = true 时触发 - 这与网格无关 - 如果您想在从下拉列表中选择项目时触发所有下拉列表,则需要为下拉列表设置 auto postback = true。
    • 下拉列表确实有 autopostback = true。它触发的是 Page_Load 方法,而不是 onselectedindexchange 方法。正如我解释的那样,我对为什么会发生这种情况做出了错误的假设。
    • 啊,好的 - 所以事件正在触发。如前所述,网格索引更改事件不会触发。但是,由于在我们的下拉选择中,我们确实通过父概念拾取了单击的索引行 - 因此您可以将 gridviewe 选择的索引设置为您从父行获得的行号。但是,正如您所注意到的 - 无论如何,现在一切都很好。但是,如果您有其他按钮和代码 - 并且希望该代码在同一行上运行 - 如果您希望设置选定的网格行,则可以 - 但到目前为止,我认为这没有必要。
    猜你喜欢
    • 1970-01-01
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 1970-01-01
    • 1970-01-01
    • 2018-12-21
    相关资源
    最近更新 更多