如何在使用BackgroundWorker时处理UI冻结

时间:2014-12-16 10:07:55

标签: c# multithreading winforms datagridview backgroundworker

我正在创建一个应用程序(logviewer),它将显示xml文件中的日志。 注意:每个xml文件都可以有一个或多个记录。我的应用程序必须将每个记录显示为DatagridView控件中的一行。

基本上它将执行以下任务:

1.来自Do_Work =>解析每个xml文件并将记录添加到列表中 2.如果列表大小达到100,则调用ProgressChanged事件以更新包含100条记录的UI(DataGridView)。 3.重复此过程,直到所有xml文件中的所有记录都附加到UI(DataGridView)

要求: 即使用户试图读取数千个文件,UI也不应该冻结。

我已经通过在DoWork事件中等待100毫秒来实现上述方案,然后在调用ProgressChanged之前出于以下原因:

1.Background thread等待(Thread.Sleep(100))100毫秒,以便UI线程可以同时更新并对用户可见(记录正在追加)。

我需要让Thread.Sleep()使UI线程呈现记录。 有没有最好的方法,我可以通过它来更新UI而不冻结?

因为如果我使用4千条记录测试我的应用程序,那么100毫秒的等待时间也无法正常工作,我的意思是如果我在表单上执行某些操作,应用程序会冻结。

但如果我将等待时间增加到500毫秒,那么它可以工作,但显示所有记录需要更多时间。

所以请建议我在使用BackgroundWorker组件时更新UI的更好方法。

这是我的代码:

注意:这是示例代码

    private void bWorkerReadXmlLogs_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {                               
            //declare a xmlLogRecords list to hold the list of log records to be displayed on the UI
            List<XmlLogRecord> lstXmlLogRecords = new List<XmlLogRecord>();

            //loop through the records sent by the GetLogDetails() function and add it to the list
            foreach (XmlLogRecord record in GetLogDetails(path))
            {
                //cancel the background thread if the cancel was requested from the user.
                if (bWorkerReadXmlLogs.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }
                //add log record to the list
                lstXmlLogRecords.Add(record);

                /*if the list contains 100 items then invoke the progresschanged event 
                where it appends the 100 items into the LogViewer UI (DataGridView)*/
                if (lstXmlLogRecords.Count % 100 == 0)
                {
                    //block/wait on background thread so that processor allocates some cycles to work on UI/Main thread to update the records on the DatagridView
                    Thread.Sleep(100); //if i wait more time like 500 ms then UI does not freeze but it takes more time

                    bWorkerReadXmlLogs.ReportProgress(0, new List<XmlLogRecord>(lstXmlLogRecords));

                    //clear the List to start/add items from the beginning.
                    lstXmlLogRecords.Clear();
                }
            }

            //invoke the ProgressChanged Event for updating the DataGridView if the List contains less than 100 items greater than 0 items
            if (lstXmlLogRecords.Count > 0)
            {                
                //Invoke ProgressChanged Event to update the records on the DataGridView
                bWorkerReadXmlLogs.ReportProgress(0, lstXmlLogRecords);
            }
        }
        catch (Exception ex)
        {
            Write_Trace(ex.Message);
        }
    }

    private void bWorkerReadXmlLogs_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {           
            try
            {
                var rowIndex = 0;
                if (e.UserState is List<XmlLogRecord>)
                {
                    //Cast the UserState object into the List<XmlLogRecord>
                    var records = e.UserState as List<XmlLogRecord>;

                    //iterate over all the records sent from DoWork event and append the each record into the LogViewer control DataGridView UI
                    foreach (var record in records)
                    {
                        //get the next row Index where the new row has to be placed.
                        rowIndex = dgvLogViewer.Rows.Count;

                        //add an emtpy row to add the empty cells into it
                        dgvLogViewer.Rows.Add();

                        //set the LogViewer properties if not set already
                        if (!IsLogviewerPropertiesSet)
                        {
                            SetLogViewerProperties();
                        }

                        //Disable the Column selection for image columns
                        DisableImageColumnsSelection(rowIndex);

                        //Add Data for normal or text cells into the LogViewer Control (DatagridView)
                        AddLogviewerTextCells(rowIndex, record);

                        //Add Icons in Image Columns into the LogViewer control (DataGridView) 
                        AddLogviewerImageCells(rowIndex, record);
                    }

                    //Sort the LogViewer control (DataGridView) by Datetime column(Index = 2) in Descending order.
                    dgvLogViewer.Sort(dgvLogViewer.Columns[MKeys.DTTIME], ListSortDirection.Descending);
                    dgvLogViewer.Columns[MKeys.DTTIME].HeaderCell.SortGlyphDirection = SortOrder.Descending;
                    if (!IsLogviewerSortingDone)
                    {
                        //call selectedindex changed event of the selected record in the datagridview
                        dgvLogViewer_SelectionChanged(null, null);
                        IsLogviewerSortingDone = true;
                    }
                }
            }
            catch (Exception ex)
            {                   
                Write_Trace(ex.Message);
            }
        }
    }

2 个答案:

答案 0 :(得分:0)

你不需要线程睡眠。如果触发ProgressChanged,则可以将此100条记录加载到GridView DataSource。然后使用GridView中的Update-Methods。

  1. 将新的100条记录添加到绑定到GridView的数据源。
  2. 然后:

    //也许在这里再次设置DataSource

    GridView.Refresh();
    

    如果这不起作用,请查看BeginInvoke()和EndInvoke()方法。

答案 1 :(得分:0)

如果您使用的是.NET Framework 4.5或更高版本,我命令使用基于任务的异步模式并利用async和await keywods(http://msdn.microsoft.com/en-us/library/hh191443.aspx)。但如果没有,你真的不需要任何线程睡觉。只需处理BackgroundWorker的ProgressChanged事件并更新网格。