微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

GridView 中的 DropDownList 中动态创建的项目不起作用

如何解决GridView 中的 DropDownList 中动态创建的项目不起作用

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

问题出在此 dropdownlist 的回发上。我显然必须使用 Gridview 中动态创建的 Items 重新创建 dropdownlist。这不起作用。回发发生并调用 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 解决方案。

解决方法

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

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

您当然可以为下拉菜单触发/触发一个事件,但您是否真的需要此处的事件完全不清楚。

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

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

    <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 - 它只在绑定过程中存在。但此信息非常有价值,因为您充分利用了您用于绑定的实际数据源。在上面,我需要来自设置下拉列表的那一行。

好的,输出现在看起来像这样:

enter image description here

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

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

好的,下一期:

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

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

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

今日小贴士: 既然我们不能选择下拉菜单并使用属性表?

然后在标记中这样做:

您可以在 OnSelectedIndexChanged=

中的标记类型

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

enter image description here

所以,点击 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>");


    }

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

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

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

无论如何,现在说我选择了上面的最后一个下拉列表,然后进行更改。

我得到这个输出:

enter image description here

总结:

只加载网格 - 加载第一页 -(您检查 IsPostBack)。

您可以拥有各种额外的回传 - 网格应该可以生存并且很好 - 根本不需要重新加载。 (GridView 内置了视图状态)。

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

我可以循环网格视图 - 对该网格的任何更改都将保留。

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

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

既然我们有了这个工作,那么我们就可以回去构建自定义用户控件了——但如果做得对,它也应该表现得正确,并且还应该能在回传后继续存在。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。