我正在创建一个应用程序(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);
}
}
}
答案 0 :(得分:0)
你不需要线程睡眠。如果触发ProgressChanged,则可以将此100条记录加载到GridView DataSource。然后使用GridView中的Update-Methods。
然后:
//也许在这里再次设置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事件并更新网格。