Here is one my list of possibilities, how to update UI from Parallel.ForEach.
First, and probably the best with IProgress:
private readonly IProgress<int> _recordCountProgress;
private readonly IProgress<string> _fileNameProgress;
...
Parallel.ForEach(Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories), options, fileName =>
{
RecordCount = Interlocked.Increment(ref _recordCount);
if (RecordCount % progressStep == 0)
{
_fileNameProgress.Report(fileName);
_recordCountProgress.Report(RecordCount);
}
});
...
IProgress<int> recordCountProgress = new Progress<int>(NumberOfFilesProcessedIProgress);
IProgress<string> fileNameProgress = new Progress<string>(FileProcessedIProgress);
notice:
if (RecordCount % progressStep == 0)
If there are only few files _recordCount will be always 0, because it is to fast, if there are too many files, without UI would be blocked.
Second with SynchronizationContext:
Parallel.ForEach(Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories), options, fileName =>
{
RecordCount = Interlocked.Increment(ref _recordCount);
if (RecordCount % progressStep == 0)
{
_syncContext?.Post(_ =>
{
FileProcessed?.Invoke(this,
new FilesProcessedSynchronizationContextEventArgs(fileName));
NumberOfFilesProcessed?.Invoke(this,
new RecordCountSynchronizationContextEventArgs(_recordCount));
}, null);
}
});
Same as previous, problem with the line:
if (RecordCount % progressStep == 0)
Third with System.Threading.Timer:
CancellationTokenSource = cancellationTokenSource;
_timer = new System.Threading.Timer(_ =>
{
if (_queue.TryDequeue(out string? fileName))
{
if (form.InvokeRequired)
{
form.BeginInvoke(() =>
{
FileProcessed?.Invoke(this, new FilesProcessedEventArgs(fileName));
NumberOfFilesProcessed?.Invoke(this, new RecordCountEventArgs(_recordCount));
});
}
else
{
FileProcessed?.Invoke(this, new FilesProcessedEventArgs(fileName));
NumberOfFilesProcessed?.Invoke(this, new RecordCountEventArgs(_recordCount));
}
}
}, null, 0, 100);
Example download from
here