C# Event handler for WPF MVVM
Your approach is wrong. It creates too complex code for a very simple and common problem. To use a sort of event aggregator to report progress is overkill.
First notice that you generally distinguish between IO bound tasks and CPU bound tasks. CPU bound tasks are executed on a background thread. The performance of CPU bound tasks can be improved by using multi threading. For example calculations are CPU bound.
CPU bound tasks should be executed in parallel using background threads.
Reading from a file or download data are IO bound tasks. The performance of IO bound tasks cannot be improved using multiple threads, but by improving the IO devices. For example a download's performance is not limited by available CPU resources, but transfer rate or other network conditions and hardware. Using a thread for IO bound tasks is a waste of resources, since IO bound operations generally require almost no CPU resources.
IO bound tasks should be executed asynchronously or event-driven.
This means you should check the library you are using to download resources for asynchronous methods and await them e.g. await DownloadAsync(). Alternatively subscribe to corresponding API events e.g. DownloadProgressChanged. Every modern UI related API exposes asynchronous methods.
Since all DispatcherObject objects are associated with a Dispatcher and each thread has its own Dispatcher, you can't use or access a DispatcherObject on a different thread then the thread the DispatcherObject was created on. Except the DispatcherObject derives from Freezable and is in a frozen state.
To access a DispatcherObject from a different thread you must use the associated Dispatcher.
The following examples show how to update e.g., a Progressbar from a background thread:
MainWindow.xaml
&lt;Window&gt;
  &lt;Progressbar x:Name=&quot;CompressionProgressbar&quot; /&gt;
&lt;/Window&gt;

MainWindow.xaml.cs
private async Task CompressData()
{
  // Start a background thread
  await Task.Run(() =&gt;
  {
    // Access the DispatcherObject from a background thread 
    // using the associated Dispatcher
    this.Dispatcher.InvokeAsync(() =&gt; this.CompressionProgressbar.Value = 33.33);
  });
}

Since .NET 4.5 the recommended approach is to use IProgress&lt;T&gt; to report progress (or delegate data back to the UI thread). The library offers a ready to use implementation, the Progress&lt;T&gt; class. The constructor captures the current SynchronizationContext to post the registered callback to it. Therefore it is important to create the instance on the UI thread and then pass it to the background thread.
private async Task CompressData()
{
  // Create an instance of Progress&lt;T&gt; using the constructor
  // to register the callback which will be executed on the current thread e.g. to report the progress
  var progressReporter = new Progress&lt;double&gt;(progressValue =&gt; this.CompressionProgressbar.Value = progressValue);

  // Start a background thread
  await Task.Run(() =&gt;
  {
    // Access the DispatcherObject from a background thread 
    // using the IProgress&lt;T&gt; instance
    progressReporter.Report(33.33);
  });
}