简体   繁体   English

WebForms嵌套用户控件数据绑定

[英]WebForms Nested User Controls Data Binding

I'm implementing something using nested user controls and am having issues related to data binding, so I created a small application to attempt to debug the behavior. 我正在使用嵌套的用户控件来实现某些功能,并且遇到了与数据绑定相关的问题,因此我创建了一个小型应用程序来尝试调试行为。

I have a default page (aspx) that contains a single user control, 'ChildControl'. 我有一个默认页面(aspx),其中包含一个用户控件'ChildControl'。 In the code behind, I am setting a couple of properties for ChildControl and calling DataBind() on it. 在后面的代码中,我为ChildControl设置了几个属性,并在其上调用了DataBind()。

In the ChildControl user control (ascx), I have a ListView of 'GrandchildControl' ListViewItems, which has a datasource bound to a property that was set within the LoadPage() method of Default.aspx. 在ChildControl用户控件(ascx)中,我有一个'GrandchildControl'ListViewItems的ListView,它的数据源绑定到在Default.aspx的LoadPage()方法中设置的属性。 I also am binding a property of GrandchildControl that will be used for a DropDownList within that user control. 我还绑定了GrandchildControl的属性,该属性将用于该用户控件中的DropDownList。 In the code behind for this user control, I have a click event handler that simply adds a new User object to a session variable and calls the LoadPage() method of Default.aspx. 在此用户控件的代码中,我有一个click事件处理程序,该事件处理程序仅将新的User对象添加到会话变量中,并调用Default.aspx的LoadPage()方法。

In the GrandchildControl user control (ascx), I am displaying the information for each bound User object. 在GrandchildControl用户控件(ascx)中,我正在显示每个绑定的User对象的信息。

I have added logs to see the order in which the events are being called. 我添加了日志,以查看事件被调用的顺序。 When I navigate to the page (not postback), the events fire in the following order as expected: 当我导航到页面(而不是回发)时,事件将按以下顺序触发:

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

However, when I click the LinkButton within ChildControl to add a new User object, the events fire in this order: 但是,当我单击ChildControl中的LinkBut​​ton以添加新的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

As you can see, the User objects are bound to the ListView after the call to the their respective page load's firing. 如您所见,User对象在调用各自页面加载的触发后绑定到ListView。 Because of this, the dropdown selections are not set correctly (they all default to the 0 index). 因此,下拉选项未正确设置(它们全部默认为0索引)。

So my question is, what causes ucChildControl.DataBind() to behave differently when the Page is initially loaded, versus when it is called from the context of the ChildControl's click handler? 所以我的问题是,是什么原因导致ucChildControl.DataBind()在初始加载Page时与从ChildControl的click处理程序的上下文中调用时有所不同?

I apologize for the long post but wanted to show as much detail as possible seeing as I have searched and have been unable to find a similar issue and resolution. 我为冗长的帖子表示歉意,但想显示尽可能多的详细信息,因为我已经搜索过并且无法找到类似的问题和解决方案。

All code is below: 所有代码如下:

Model: 模型:

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 front end: aspx前端:

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

aspx code behind: 后面的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\n";

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

Child ascx front end: 子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>

Child ascx code behind: 子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\n";
    }

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

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

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

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

        usersAdded.Add(new User());

        Session["usersAdded"] = usersAdded;

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

Grandchild ascx front end: 孙子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>

Grandchild ascx code behind: 孙子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\n";

        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\n";
    }
}

In case anyone comes across this question and is stuck on a similar issue, I have solved the issue. 如果有人遇到这个问题并陷入类似的问题,我已经解决了这个问题。

The reason all dropdown selections revert back to the 0 index is because when data binding after adding an additional user, the page load event of each list item is called BEFORE the data bound event (as seen in the original question). 所有下拉选择都还原为0索引的原因是,当添加其他用户后进行数据绑定时,每个列表项的页面加载事件在数据绑定事件之前被称为(如原始问题所示)。 Because of this, the following happens: 因此,发生以下情况:

  1. GrandchildControl page loads. GrandchildControl页面加载。 User property is null at this point as it is not set until AFTER page load. 用户属性此时为null,因为直到页面加载后才设置。 The only thing that happens at this point is adding the new list item to the dropdown. 此时唯一发生的事情是将新列表项添加到下拉列表中。

  2. After page load, all front end properties are binded and the ItemDataBound event is called. 页面加载后,将绑定所有前端属性,并调用ItemDataBound事件。 When binding the dropdown datasource, the list item that was previously inserted is overwritten and the selected value is no longer set, therefore it defaults to the 0 index. 绑定下拉数据源时,先前插入的列表项将被覆盖,并且不再设置所选值,因此默认为0索引。

Therefore, the insertion of the default dropdown selection and assignment of the selected value needs to be done in the ItemDataBound event. 因此,需要在ItemDataBound事件中完成默认下拉选择的插入和所选值的分配。 I have attached all code changes below. 我已附上以下所有代码更改。

Child ascx code behind: 子ascx代码背后:

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

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

Grandchild ascx code behind: 孙子ascx代码背后:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM