【问题标题】:WebForms Nested User Controls Data BindingWebForms 嵌套用户控件数据绑定
【发布时间】:2018-06-10 02:12:07
【问题描述】:

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

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

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

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

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

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

但是,当我单击 ChildControl 中的 LinkBut​​ton 以添加新的用户对象时,事件按以下顺序触发:

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

如您所见,在调用各自的页面加载触发后,用户对象被绑定到 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\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();
    }
}

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

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

【问题讨论】:

    标签: c# asp.net events data-binding webforms


    【解决方案1】:

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

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

    1. GrandchildControl 页面加载。此时用户属性为空 直到页面加载后才设置。唯一发生在 这一点是将新的列表项添加到下拉列表中。

    2. 页面加载后,所有前端属性都被绑定,并且 调用 ItemDataBound 事件。绑定下拉数据源时, 之前插入的列表项被覆盖,并且 所选值不再设置,因此默认为 0 索引。

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

    后面的子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();
    }
    

    孙子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;
        }
    }
    

    【讨论】:

    • 这个答案很有帮助,但我有一个问题。我的孙子控件并不总是嵌套在用户控件中。有时它直接在页面上。换句话说,有时它是一个孩子而不是一个孙子。这个解决方案还能用吗?
    • 可以,只要把item数据绑定事件放在父aspx中,而不是子控件中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-29
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多