【发布时间】:2014-12-16 10:07:54
【问题描述】:
我正在创建一个应用程序(logviewer),它将显示来自 xml 文件的日志。
注意:每个 xml 文件可以有一个或多个记录。我的应用程序必须在 DatagridView 控件中将每条记录显示为一行。
基本上它将执行以下任务:
1.from Do_Work => 解析每个 xml 文件并将记录添加到列表中。
2.如果列表大小达到100,则调用ProgressChanged事件以更新100条记录的UI(DataGridView)。
3.重复此过程,直到所有xml文件中的所有记录都附加到UI(DataGridView)
要求: 即使用户尝试读取数千个文件,UI 也不应冻结。
我通过在DoWork 事件中等待100 毫秒然后调用ProgressChanged 来实现上述场景,原因如下:
1.后台线程等待(Thread.Sleep(100))100毫秒,以便UI线程可以同时更新并且对用户可见(记录正在追加)。
我是否需要Thread.Sleep() 才能使 UI 线程呈现记录。
有什么最好的方法可以让我在不冻结的情况下更新 UI?
因为如果我用 4000 条记录测试我的应用程序,那么 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);
}
}
}
【问题讨论】:
-
BackgroundWorker.ProgressChanged是做什么的?您发布的代码与 UI 无关,除了bWorkerReadXmlLogs.ReportProgress -
@SriramSakthivel:它将遍历所有 100 条记录并将每条记录附加到
DataGridView -
我建议您在更新网格时使用DataGridView.SuspendLayout
-
你能把那个代码贴出来吗?这确实是最重要的。
-
@bansi
SuspendLayout不是为了那个目的。其目的是在进行批量更新时暂停子控件的重新排列。但是DatagridView没有孩子。
标签: c# multithreading winforms datagridview backgroundworker