Instead of tracking the threads directly have the UI bind to a ObserveableCollection<ProgressDetail>
each representing the progress, then in your loop have it add an item to the collection when it starts then remove it from the collection when it ends.
One thing you must be careful of is thread safety, ObseveableCollection
is not thread safe so you must interact with it only in thread safe ways, the easiest way to do this is make all of the adds and removals of ProgressDetail
objects on the UI thread. This also has the added benefit of capturing the SynchronizationContext of the UI thread when you create the Progress
object.
public ObserveableCollection<ProgressDetail> ProgressCollection {get; private set;}
public void CopyFiles(string dir)
{
var dispatcher = Application.Current.Dispatcher;
Parallel.ForEach(Directory.GetFiles(dir).ToList(), file =>
{
ProgressDetail progressDetail = null;
dispatcher.Invoke(() =>
{
// We make the `Progress` object on the UI thread so it can capture the
// SynchronizationContext during its construction.
progressDetail = new ProgressDetail(file);
ProgressCollection.Add(progressDetail);
}
XCopy.Copy(file, file.Replace(_destDetail.Source, _destDetail.Dest),
true, false, progressDetail.ProgressReporter);
dispatcher.Invoke(() => ProgressCollection.Remove(progressDetail);
});
}
public sealed class ProgressDetail : INotifyPropertyChanged
{
private double _progressPercentage;
public ProgressDetail(string fileName)
{
FileName = fileName;
ProgressReporter = new Progress<double>(OnProgressReported);
}
public string FileName { get; private set; }
public IProgress<double> ProgressReporter { get; private set; }
public double ProgressPercentage
{
get { return _progressPercentage; }
private set
{
if (value.Equals(_progressPercentage)) return;
_progressPercentage = value;
OnPropertyChanged();
}
}
private void OnProgressReported(double progress)
{
ProgressPercentage = progress;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var temp = PropertyChanged;
if(temp != null)
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
See this answer for a example XCopy
class that would copy with progress. I have made the assumption that the signature of Copy
has been changed to
public static void Copy(string source, string destination, bool overwrite, bool nobuffering, IProgress<double> handler)
but I leave that actual change as a exercise for the reader.
UPDATE: I have updated the above code example to expose a public property ProgressPercentage
which can be bound to and raises proper events. I have also moved the listening of the Progress
event in to the internals of the ProgressDetail
class.