WebForms嵌套用户控件数据绑定

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebForms嵌套用户控件数据绑定相关的知识,希望对你有一定的参考价值。

我正在使用嵌套的用户控件实现一些东西,并且遇到了与数据绑定相关的问题,所以我创建了一个小应用程序来尝试调试行为。

我有一个包含单个用户控件的默认页面(aspx),'ChildControl'。在后面的代码中,我为ChildControl设置了几个属性并在其上调用DataBind()。

在ChildControl用户控件(ascx)中,我有一个ListView的'GrandchildControl'ListViewItems,它有一个数据源绑定到在Default.aspx的LoadPage()方法中设置的属性。我还绑定了GrandchildControl的一个属性,该属性将用于该用户控件中的DropDownList。在这个用户控件的代码中,我有一个click事件处理程序,它只是将一个新的User对象添加到一个会话变量并调用Default.aspx的LoadPage()方法。

在GrandchildControl用户控件(ascx)中,我显示每个绑定的User对象的信息。

我添加了日志以查看调用事件的顺序。当我导航到页面(而不是回发)时,事件按预期按以下顺序触发:

DEFAULT PAGE LOAD
GRANDCHILD CONTROL ITEM DATA BOUND
GRANDCHILD CONTROL ITEM DATA BOUND
CHILD CONTROL PAGE LOAD
GRANDCHILD CONTROL PAGE LOAD
GRANDCHILD CONTROL PAGE LOAD

但是,当我单击Child Control中的“链接”按钮添加新的User对象时,事件将按以下顺序触发:

DEFAULT PAGE LOAD
CHILD CONTROL PAGE LOAD
GRANDCHILD CONTROL PAGE LOAD
GRANDCHILD CONTROL PAGE LOAD
ADD INCOME CLICKED
GRANDCHILD CONTROL PAGE LOAD
GRANDCHILD CONTROL ITEM DATA BOUND
GRANDCHILD CONTROL PAGE LOAD
GRANDCHILD CONTROL ITEM DATA BOUND
GRANDCHILD CONTROL PAGE LOAD
GRANDCHILD CONTROL ITEM DATA BOUND

如您所见,在调用各自的页面加载触发后,User对象被绑定到ListView。因此,下拉选项未正确设置(它们都默认为0索引)。

所以我的问题是,是什么原因导致ucChildControl.DataBind()在最初加载页面时表现不同,而不是从ChildControl的单击处理程序的上下文调用它?

我为这篇长篇文章道歉,但希望尽可能详细地展示我已经搜索过的并且无法找到类似的问题和解决方案。

所有代码如下:

模型:

public class DropdownContainer
{
    public List<DropdownValue> DropdownValues { get; set; }
}

public class DropdownValue
{
    public string DisplayText { get; set; }
    public string ID { get; set; }
}

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string AgeGroup { get; set; }
}

aspx前端:

<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
    <uc1:ChildControl runat="server" ID="ucChildControl"></uc1:ChildControl>
</asp:Content>

aspx代码背后:

public partial class Default : Page
{
    private DropdownContainer _ageGroupContainer = new DropdownContainer()
    {
        DropdownValues = new List<DropdownValue>()
        {
            new DropdownValue()
            {
                DisplayText = "18-25",
                ID = "1"
            },
            new DropdownValue()
            {
                DisplayText = "26-35",
                ID = "2"
            },
            new DropdownValue()
            {
                DisplayText = "36-45",
                ID = "3"
            }
        }
    };

    private List<User> _userDataSource = new List<User>()
    {
        new User()
        {
            FirstName = "Mickey",
            LastName = "Mouse",
            AgeGroup = "1"
        },
        new User()
        {
            FirstName = "Daffy",
            LastName = "Duck",
            AgeGroup = "3"
        }
    };

    public string Log { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        Log += "DEFAULT PAGE LOAD
";

        if (!Page.IsPostBack)
        {
            LoadPage();
        }
    }

    public void LoadPage()
    {
        List<User> lstUserTemp = _userDataSource;

        if (Session["usersAdded"] != null)
        {
            lstUserTemp.AddRange((List<User>)Session["usersAdded"]);
        }

        ucChildControl.UserDataSource = lstUserTemp;
        ucChildControl.AgeGroupDataSource = _ageGroupContainer;
        ucChildControl.DataBind();
    }
}

儿童ascx前端:

<div>
    <asp:ListView runat="server" ID="lvChild" DataSource='<%# UserDataSource %>' OnItemDataBound="lvChild_ItemDataBound">
        <ItemTemplate>
            <uc2:GrandchildControl runat="server" ID="ucGrandchildControl" User='<%# Container.DataItem %>' AgeGroupDataSource='<%# AgeGroupDataSource %>'></uc2:GrandchildControl>
            <br />
        </ItemTemplate>
    </asp:ListView>
</div>
<asp:LinkButton runat="server" ID="lbtnAddUser" OnClick="lbtnAddUser_Click" Text="Add User"></asp:LinkButton>

子ascx代码背后:

public partial class ChildControl : System.Web.UI.UserControl
{
    public List<User> UserDataSource { get; set; }

    public DropdownContainer AgeGroupDataSource { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        ((Default)Page).Log += "CHILD CONTROL PAGE LOAD
";
    }

    protected void lvChild_ItemDataBound(object sender, ListViewItemEventArgs e)
    {
        ((Default)Page).Log += "GRANDCHILD CONTROL ITEM DATA BOUND
";
    }

    protected void lbtnAddUser_Click(object sender, EventArgs e)
    {
        ((Default)Page).Log += "ADD USER CLICKED
";

        List<User> usersAdded = (List<User>)Session["usersAdded"];

        if (usersAdded == null)
        {
            usersAdded = new List<User>();
        }

        usersAdded.Add(new User());

        Session["usersAdded"] = usersAdded;

        ((Default)Page).LoadPage();
    }
}

孙子ascx前端:

<div>
    <asp:Label runat="server" Text="User Name: "></asp:Label>
    <asp:TextBox runat="server" ID="txtUserName" Text='<%# string.Format(@"{0} {1}", User.FirstName, User.LastName) %>'></asp:TextBox>
</div>
<div>
    <asp:Label runat="server" Text="Age Group: "></asp:Label>
    <asp:DropDownList runat="server" ID="ddlAgeGroup" DataSource='<%# AgeGroupDataSource.DropdownValues.OrderBy(x => Convert.ToInt32(x.ID)) %>' DataTextField="DisplayText" DataValueField="ID" OnSelectedIndexChanged="ddlAgeGroup_SelectedIndexChanged" AutoPostBack="true"></asp:DropDownList>
</div>

孙子ascx代码背后:

public partial class GrandchildControl : System.Web.UI.UserControl
{
    public User User { get; set; }

    public DropdownContainer AgeGroupDataSource { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        ((Default)Page).Log += "GRANDCHILD CONTROL PAGE LOAD
";

        if (User != null)
        {
            ddlAgeGroup.SelectedValue = User.AgeGroup;
        }

        ddlAgeGroup.Items.Insert(0, new ListItem() { Text = "SELECT AN AGE GROUP", Value = string.Empty });
    }

    protected void ddlAgeGroup_SelectedIndexChanged(object sender, EventArgs e)
    {
        ((Default)Page).Log += "GRANDCHILD CONTROL SELECTED INDEX CHANGED
";
    }
}
答案

如果有人遇到这个问题并且遇到类似的问题,我已经解决了这个问题。

所有下拉选择恢复为0索引的原因是因为在添加其他用户之后进行数据绑定时,每个列表项的页面加载事件都在数据绑定事件之前调用(如原始问题中所示)。因此,会发生以下情况:

  1. 加载GrandchildControl页面。此时User属性为null,因为它在AFTER页面加载之前未设置。此时唯一发生的事情是将新列表项添加到下拉列表中。
  2. 页面加载后,所有前端属性都绑定,并调用ItemDataBound事件。绑定下拉数据源时,先前插入的列表项将被覆盖,并且不再设置所选值,因此默认为0索引。

因此,需要在ItemDataBound事件中完成默认下拉列表选择的插入和所选值的分配。我已在下面附加了所有代码更改。

子ascx代码背后:

protected void lvChild_ItemDataBound(object sender, ListViewItemEventArgs e)
{
    ((Default)Page).Log += "GRANDCHILD CONTROL ITEM DATA BOUND
";

    //Add call to 'InitializeControls' method of grandchild user control.
    ((GrandchildControl)e.Item.FindControl("ucGrandchildControl")).InitializeControls();
}

孙子ascx代码背后:

//Remove initialization logic from page load.
protected void Page_Load(object sender, EventArgs e)
{
    ((Default)Page).Log += "GRANDCHILD CONTROL PAGE LOAD
";
}

//This method has been added to be called from the ItemDataBound event.
public void InitializeControls()
{
    ddlAgeGroup.Items.Insert(0, new ListItem() { Text = "SELECT AN AGE GROUP", Value = string.Empty });

    if (User != null)
    {
        ddlAgeGroup.SelectedValue = User.AgeGroup;
    }
}

以上是关于WebForms嵌套用户控件数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

WPF,mvvm,在combobox中嵌套了checkbox控件,数据绑定完成后,无法实现双向绑定

Repeater 控件的嵌套使用

Repeater使用方法---基础数据绑定+多级嵌套

Repeater使用方法---基础数据绑定+多级嵌套

ASP.NET WebForms:Repeater 控件中的图像尺寸模式

控件嵌套后清除方法