异步填充ASP.NET ListBox而不阻止UI

时间:2014-02-24 14:02:45

标签: c# asp.net multithreading .net-3.5 backgroundworker

我在ASP.NET用户控件中有一个列表框,我想填充从数据库中检索到的可能很大的List<string>(最多50k项)。在填充列表框时(在页面加载事件中),我不想阻止UI。在完成列表框填充之前,使用同步代码不会显示页面:

List<string> items = GetItemsFromDB();    
foreach (var item in items) // can take some time, blocks UI
{
    if (shouldItemBeListed(item))
        ListBox1.Items.Add(item);
}

我来自桌面应用程序背景,其中使用例如BackgroundWorker - 用户将看到实时填充新项目的列表框。 Web的东西似乎有点复杂,我想就什么是最好的方法来提出一些建议。

这是我试过的:

1。创建后台线程。 不能很好地工作 - 与桌面(WinForms)版本相比,一切仍然没有响应,并且列表没有按预期更新/刷新:

Thread thread = new Thread(new ThreadStart(Populate));
thread.IsBackground = true;
thread.Start(); 

private void Populate()
{
    List<string> items = GetItemsFromDB();    
    foreach (var item in items)
    {
        if (shouldItemBeListed(item))
            ListBox1.Items.Add(item);
    }
}

2。 BackgroundWorker线程。比1更好一点。(至少列表框正在更新新项目),但仍然非常反应迟钝。

var bw = new BackgroundWorker();
bw.DoWork += (o, args) => Populate();
bw.RunWorkerAsync();

我总是可以将列表框项目分成更小的组,并根据用户的要求填充更多,但这通常如何正确实现呢?

2 个答案:

答案 0 :(得分:3)

由于asp.net生命周期(以及一般的网络),你不能使用这种方法。基本上,对webserver的每个请求都会产生你的asp.net页面类,生成html,发送给客户端,然后关闭实例。使用多线程技术无济于事,因为您将更改控制树 AFTER 服务器已发送答案。

要解决您的问题,您应该使用<asp:hidden runat='server'>字段。

然后,使用某种客户端脚本,从Web服务调用中动态填充下拉列表。

使用jQuery(来自内存)这样的东西:

<select id='ddl' onchange='document.getElementById("<%= hidValue.ClientID %>").value=this.value'>
    <option value=''>Loading...</option>
</select>
<asp:Hidden runat='server' id='hidValue' />

$(function(){
   $.ajax("... your api endpoit").then(function(result){
       var items = result.items; // actual implementation will change
       var ddl = $(ddl);
       ddl.html(''); // remove the 'loading' option.
       foreach(var item in items) {
           var option = $("<option>");
           option.attr("value",item.value);
           option.text(item.text);
           ddl.append(option);
       }    
   });
});

不使用<asp:DropDownList>的原因是您无法在回发之间更改其子级。所以我们使用一个纯客户端,并在隐藏控件中同步更改,这可以在回发时读取。

答案 1 :(得分:0)

达蒙说得对,因为你正在寻找ajax,它应该为你提供你想要的功能。我使用gridview找到了一个示例frmo MSDN,但它应该具有适应性:

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Collections.Generic" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Enter New Employees</title>
<script runat="server">
    private List<Employee> EmployeeList;

    protected void Page_Load()
    {
        if (!IsPostBack)
        {
            EmployeeList = new List<Employee>();
            EmployeeList.Add(new Employee(1, "Jump", "Dan"));
            EmployeeList.Add(new Employee(2, "Kirwan", "Yvette"));
            ViewState["EmployeeList"] = EmployeeList;
        }
        else
            EmployeeList = (List<Employee>)ViewState["EmployeeList"];

        EmployeesGridView.DataSource = EmployeeList;
        EmployeesGridView.DataBind();
    }

    protected void InsertButton_Click(object sender, EventArgs e)
    {
        if (String.IsNullOrEmpty(FirstNameTextBox.Text) ||
           String.IsNullOrEmpty(LastNameTextBox.Text)) { return; }

        int employeeID = EmployeeList[EmployeeList.Count-1].EmployeeID + 1;

        string lastName = Server.HtmlEncode(FirstNameTextBox.Text);
        string firstName = Server.HtmlEncode(LastNameTextBox.Text);

        FirstNameTextBox.Text = String.Empty;
        LastNameTextBox.Text = String.Empty;

        EmployeeList.Add(new Employee(employeeID, lastName, firstName));
        ViewState["EmployeeList"] = EmployeeList;

        EmployeesGridView.DataBind();
        EmployeesGridView.PageIndex = EmployeesGridView.PageCount;
    }

    protected void CancelButton_Click(object sender, EventArgs e)
    {
        FirstNameTextBox.Text = String.Empty;
        LastNameTextBox.Text = String.Empty;
    }

    [Serializable]
    public class Employee
    {
        private int _employeeID;
        private string _lastName;
        private string _firstName;

        public int EmployeeID
        {
            get { return _employeeID; }
        }

        public string LastName
        {
            get { return _lastName; }
        }

        public string FirstName
        {
            get { return _firstName; }
        }

        public Employee(int employeeID, string lastName, string firstName)
        {
            _employeeID = employeeID;
            _lastName = lastName;
            _firstName = firstName;
        }
    }

</script>
</head>
<body>
<form id="form1" runat="server">
<div>
    &nbsp;</div>
    <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true"      />
    <table>
        <tr>
            <td style="height: 206px" valign="top">
                <asp:UpdatePanel ID="InsertEmployeeUpdatePanel" runat="server"     UpdateMode="Conditional">
                    <ContentTemplate>
                      <table cellpadding="2" border="0" style="background-    color:#7C6F57">
                        <tr>
                          <td><asp:Label ID="FirstNameLabel" runat="server"     AssociatedControlID="FirstNameTextBox" 
                                         Text="First Name" ForeColor="White" /></td>
                          <td><asp:TextBox runat="server" ID="FirstNameTextBox" /></td>
                        </tr>
                        <tr>
                          <td><asp:Label ID="LastNameLabel" runat="server"     AssociatedControlID="LastNameTextBox" 
                                         Text="Last Name" ForeColor="White" /></td>
                          <td><asp:TextBox runat="server" ID="LastNameTextBox" /></td>
                        </tr>
                        <tr>
                          <td></td>
                          <td>
                            <asp:LinkButton ID="InsertButton" runat="server"     Text="Insert" OnClick="InsertButton_Click" ForeColor="White" />
                            <asp:LinkButton ID="Cancelbutton" runat="server"     Text="Cancel" OnClick="CancelButton_Click" ForeColor="White" />
                          </td>
                        </tr>
                      </table>
                      <asp:Label runat="server" ID="InputTimeLabel"><%=DateTime.Now %>    </asp:Label>
                    </ContentTemplate>
                </asp:UpdatePanel>
            </td>
            <td style="height: 206px" valign="top">
                <asp:UpdatePanel ID="EmployeesUpdatePanel" runat="server"     UpdateMode="Conditional">
                    <ContentTemplate>
                        <asp:GridView ID="EmployeesGridView" runat="server"     BackColor="LightGoldenrodYellow" BorderColor="Tan"
                             BorderWidth="1px" CellPadding="2" ForeColor="Black"     GridLines="None" AutoGenerateColumns="False">
                            <FooterStyle BackColor="Tan" />
                            <SelectedRowStyle BackColor="DarkSlateBlue"     ForeColor="GhostWhite" />
                            <PagerStyle BackColor="PaleGoldenrod"     ForeColor="DarkSlateBlue" HorizontalAlign="Center" />
                            <HeaderStyle BackColor="Tan" Font-Bold="True" />
                            <AlternatingRowStyle BackColor="PaleGoldenrod" />
                            <Columns>
                                <asp:BoundField DataField="EmployeeID"     HeaderText="Employee ID" />
                                <asp:BoundField DataField="LastName" HeaderText="Last     Name" />
                                <asp:BoundField DataField="FirstName" HeaderText="First     Name" />
                            </Columns>
                            <PagerSettings PageButtonCount="5" />
                        </asp:GridView>
                        <asp:Label runat="server" ID="ListTimeLabel"><%=DateTime.Now %>    </asp:Label>
                    </ContentTemplate>
                    <Triggers>
                        <asp:AsyncPostBackTrigger ControlID="InsertButton"     EventName="Click" />
                    </Triggers>
                </asp:UpdatePanel>
            </td>
        </tr>
    </table>
</form>
</body>
</html>