如何在方法结束前在UI上更新GridView

时间:2016-04-19 21:29:59

标签: asp.net gridview datatable

我在SharePoint中运行一个小应用程序,用于生成我们公司中某人所需的数据。简而言之,它将数据从Azure数据库提取到数据表中,然后将该数据表转换为excel。这可能需要大约一两分钟,有时,因为我们在新西兰并且在美国使用远程服务器,他们会得到超时,其中一个工作表无法加载。

因此,它在构建excel时的作用是遍历供应商列表,以及获取每个数据的财务周列表,并在每个数据表的Excel中创建单独的工作表。理想情况下,我想在用户看到的网格视图中添加新行, 正在构建报告 ,说明该财务周和供应商是否成功添加或不添加,因为excel报告在后端创建。这将允许用户更多地了解进度,并允许他们知道是否存在问题而不是猜测。

这是很多代码所以我会尽力向您展示相关部分。

拉取并创建Excel的方法

    public void excelThreadCall()
    {
        DataTable updateDataTable = new DataTable();
        gridView.DataSource = updateDataTable;
        //Payments only download chosen Financial Week
        using (XLWorkbook workbook = new XLWorkbook())
        {
            //gradeWeek = selectedGradeWeek.SelectedValue;
            foreach (ListItem supplier in selectedSuppliers.Items)
            {
                if (supplier.Selected)
                {
                    foreach (ListItem fWeek in selectedfWeeks.Items)
                    {
                        if (fWeek.Selected)
                        {
                            string checkEmptyTableSQL = @"SELECT COUNT(*) FROM FleshvGraded WHERE Supplier_Code = '" + supplier.Value + "' AND PO_Revision = " + fWeek.Value;
                            int rowCount = Convert.ToInt32(getVariable(checkEmptyTableSQL));

                            if (rowCount > 0)
                            {

                                foreach (ListItem report in selectedReports.Items)
                                {
                                    //SQL Strings
                                    string sqlIntakeDate = @"SELECT Week_Ending_Date FROM Fiscal_Calendar WHERE Fiscal_Week = LEFT(" + fWeek + ", 2) AND Fiscal_Year = CONCAT(20, RIGHT(" + fWeek + ", 2))";
                                    string sqlPO = @"SELECT DISTINCT PO_No FROM FvGSummaryAll WHERE Supplier_Code = '" + supplier.Value + "' AND f_Week = " + fWeek.Value;
                                    string sqlAllSerials = "SELECT * FROM FvGData WHERE Supplier_Code = '" + supplier.Value + "' AND f_Week = " + fWeek.Value

                                    //variables
                                    DateTime weekEnding = Convert.ToDateTime(getVariable(sqlIntakeDate));
                                    DateTime weekStarting = weekEnding.AddDays(-5);
                                    string fWeekString = fWeek.ToString();
                                    string poNoString = getVariable(sqlPO).ToString();
                                    string intakeDateString = weekStarting.Day + "/" + weekStarting.Month + "/" + weekStarting.Year + " to " + weekEnding.Day + "/" + weekEnding.Month + "/" + weekEnding.Year;

                                    //adds summary variables to dictionary
                                    Dictionary<string, string> summaryVariablesDict = new Dictionary<string, string>();
                                    summaryVariablesDict.Add("f Week", fWeekString);
                                    //other values added to Dict

                                    //Adds WorkSheets based on above data
                                    if (report.Selected && report.Value.Equals("allserials"))
                                    {
                                        string worksheetName = supplier.Value + " Data " + fWeek.Value;
                                        DataTable dataTable = getDataTable(sqlAllSerials);
                                        createWorkSheet(workbook, worksheetName, dataTable);
                                    }
                                    //Other Reports follow

                                    **//what I hope to do - need this to show in the grid view immediatley not end of method
                                    updateDataTable.Rows.Add(suppler, fweek, "successful");
                                    gridView.DataBind();**

                                }
                            }
                        }
                    }
                }
            }
            workbook.SaveAs(filePath);
        }
    }

所以目前这个存在于另一个类中,但是将它移动到aspx页面并没有问题,所以我已经自由地向你展示了我在这个方法中需要做什么。因此,如果它在这方面没有完全意义(即我不会在方法中正常声明网格的数据源)。

我遇到的问题是,在通过回发更新网格视图之前,它将等到方法结束,然后用户立即获取所有内容。我希望有一种方法可以在每次迭代时甚至每隔几秒更新一次gridview,如果我们使用计时器,但无法找到实现这一目的的方法。

长话短说,如何从这种方法更新gridview,其中结果立即出现在用户UI上,而不是等到方法结束。

1 个答案:

答案 0 :(得分:0)

我会沿着这些方向做点什么:

  1. 首次加载页面时,启动后台线程以开始构建电子表格。
  2. 当页面加载时,调用一些启动回调的JavaScript。
  3. 在回调调用的代码隐藏方法中,检查构建过程的状态。让该进程维护一个字符串列表,每个字符串代表一个表中行的HTML。
  4. 让页面(通过JavaScript)每隔几秒钟执行一次回调。该回调将获得当前的行列表。页面上的JavaScript将收到响应并更新呈现的表以包含新行。
  5. 完成电子表格后(或发生中止创建过程的错误时),向用户显示成功或失败消息。
  6. 如果它有用,我可以提供一些简单的回调样本来帮助你。

    编辑:添加了代码示例:

    标记:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CallBackWebForm.Default" %>
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
    
        <script type="text/javascript">
            var callbackFrequency = 2000;
    
            // Callback javascript
            //   To make callback to server, call CallServer();
    
            // Receive response from server after callback
            function ReceiveServerData(arg, context) {
                // Parse the JSON that we got from the server
                args = JSON.parse(arg);
    
                // Add rows to table
                $.each(args.TableRows, function (index, value) {
                    $('#table1').append(value);
                });
    
                // If we're done, show a message
                if (args.DoneLoadingSpreadsheet)
                    $('#doneDiv').show();
    
                    // Otherwise, start a timer to call back again
                else
                    window.setTimeout(function () { CallServer(); }, callbackFrequency);
            }
    
            $(document).ready(function() {
                // Start the callback loop
                window.setTimeout(function () { CallServer(); }, callbackFrequency);
            });
        </script>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                Sample page with some progress-y stuff
            </div>
    
            <table id="table1">
                <tr>
                    <th>Col 1</th>
                    <th>Col 2</th>
                    <th>Col 3</th>
                </tr>
                <!-- Rows inserted by Javascript will go here -->
            </table>
    
            <div id="doneDiv" style="display: none;">
                All done!
            </div>
        </form>
    </body>
    </html>
    

    代码隐藏:

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.UI;
    using Newtonsoft.Json;
    
    namespace CallBackWebForm
    {
        public partial class Default : System.Web.UI.Page, ICallbackEventHandler
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                // Setup Callback javascript so that page can initiate callbacks and receive callback responses
                CreateClientSideCallbackFunction();
    
                if (!Page.IsPostBack)
                    StartBuildingSpreadsheetTask();
            }
    
            #region Callback
            private void CreateClientSideCallbackFunction()
            {
                var cm = Page.ClientScript;
    
                // The Javascript function in the markup must exactly match the function name as entered below (ReceiveServerData)
                var cbReference = cm.GetCallbackEventReference(this, "arg", "ReceiveServerData", "");
    
                // The Javascript function to be placed in the markup which will be used to initiate the callback
                var callbackScript = "function CallServer(arg, context) {" + cbReference + "; }";
                cm.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);
            }
    
            /// <summary>
            /// Called when CallServer(arg, context) is called in javascript on the page
            /// </summary>
            /// <param name="eventArgument">Not used, but must be passed</param>
            public void RaiseCallbackEvent(string eventArgument)
            {
    
            }
    
            /// <summary>
            /// Called at the end of a callback; provides the response/result to the client
            /// </summary>
            /// <returns>JSON string representing an instance of the DataTransferClass</returns>
            public string GetCallbackResult()
            {
                // Serialize the DataTransferObject, then delete all TableRows so we don't send them to the browser again
                // Note: this is not currently thread-safe. You should add some sort of locking mechanism so the background thread 
                //       doesn't modify the TableRows list while we're serializing it and deleting from it.
                var dtoJson = JsonConvert.SerializeObject(DataTransferObject);
                DataTransferObject.TableRows.Clear();
                return dtoJson;
            }
    
            public class DataTransferClass
            {
                public bool DoneLoadingSpreadsheet { get; set; }
                public List<string> TableRows { get; set; }
            }
            #endregion Callback
    
            #region Background Task
            // Sessions have unique IDs, but individual page views don't. So, create one for this page view.
            private string ViewID
            {
                get
                {
                    if (string.IsNullOrEmpty(ViewState["_viewID"] as string))
                        ViewState["_viewID"] = Guid.NewGuid().ToString();
                    return ViewState["_viewID"] as string;
                }
            }
    
            // Store all DataTransfer data and token sources in static dictionaries so the background task can get to them
            private static Dictionary<string, DataTransferClass> DataTransferDictionary = new Dictionary<string, DataTransferClass>();
            private static Dictionary<string, CancellationTokenSource> TokenSourcesDictionary = new Dictionary<string, CancellationTokenSource>();
    
            // Make the values in the dictionaries for this View easily accessible via Properties
            private DataTransferClass DataTransferObject
            {
                get
                {
                    if (DataTransferDictionary.ContainsKey(ViewID))
                        return DataTransferDictionary[ViewID];
                    else
                        return null;
                }
                set
                {
                    if (DataTransferDictionary.ContainsKey(ViewID))
                        DataTransferDictionary[ViewID] = value;
                    else
                        DataTransferDictionary.Add(ViewID, value);
                }
            }
    
            private CancellationTokenSource TokenSource
            {
                get
                {
                    if (TokenSourcesDictionary.ContainsKey(ViewID))
                        return TokenSourcesDictionary[ViewID];
                    else
                        return null;
                }
                set
                {
                    if (TokenSourcesDictionary.ContainsKey(ViewID))
                        TokenSourcesDictionary[ViewID] = value;
                    else
                        TokenSourcesDictionary.Add(ViewID, value);
                }
            }
    
            private void StartBuildingSpreadsheetTask()
            {
                DataTransferObject = new DataTransferClass() { DoneLoadingSpreadsheet = false, TableRows = new List<string>() };
                TokenSource = new CancellationTokenSource();
                var token = TokenSource.Token;
                (new TaskFactory()).StartNew(() => BuildSpreadsheet(ViewID, token), token);
            }
    
            private void BuildSpreadsheet(string viewID, CancellationToken token)
            {
                // Simulate work. Update DataTransferObject every 5 seconds, finish after 30 seconds (6 iterations with 5 second wait);
                for (int i = 0; i < 6; i++)
                {
                    // Work for 5 seconds
                    System.Threading.Thread.Sleep(5000);
    
                    // Update DataTransferObject with new row (don't use the 'DataTransferObject' property; it relies up the 'ViewID' property, which in 
                    // turn relies upon ViewState, which isn't available from a background thread).
                    DataTransferDictionary[viewID].TableRows.Add("<tr><td>Val " + i + "</td><td>Val " + (i * 10) + "</td><td>Val " + (i * 100) + "</td></tr>");
                }
    
                // All done; update DataTransferObject
                DataTransferDictionary[viewID].DoneLoadingSpreadsheet = true;
            }
            #endregion Background Task
        }
    }
    

    一对夫妇注意到:

    • 添加Json.Net NuGet包(Newtonsoft)
    • 请注意,页面类实现了ICallbackEventHandler接口

    编辑2:更新了顶部的建议流程,以匹配我在代码示例中实际执行的操作。