0

I have a multi-assembly project with WPF as the client using the Galasoft MVVM Toolkit. I would like to know how to databind a ProgressControl's "Value" property to a BackgroundWorker's ProgressChanged event.

There are other articles that deal with this situation; however, they only deal with the Worker being situated in the VM. My Worker is ideally located in the DataService Repository which does the bulk of the grunt work as will be shown in the code below.

Here's what I got:

In the View, the ProgressControl XAML is this:

<ProgressBar Maximum="{Binding NumberOfPublications}" 
                         Minimum="0" 
                         Height="50" 
                         Margin="10"
                         Value="{Binding Source={StaticResource ConverterEntity},Path=ProgressValue, Mode=OneWay}"
                         Visibility="{Binding Source={StaticResource ConverterEntity}, Path=IsLengthyOperation, Converter={StaticResource Bool2InverseVisibility}}">

The Binding Source is in another Assembly, like this:

public class ConverterEntity : ViewModelBase
{
.
.
public int ProgressValue
    {
        get
        {
            return _progressValue;
        }
        set
        {
            _progressValue = value;
            RaisePropertyChanged( ProgressValuePropertyChanged );
        }
    }

Back in the App.xaml, I have put the reference necessary in a Windows Resource, as shown:

 <Application.Resources>

    <vc:ConverterEntity x:Key="ConverterEntity" />

Clearly, I can see that although the View xaml builds fine, the "vc" reference is not used... just wondering how it builds correctly, even though the ProgressBar doesn't need the "vc" nomenclature.

Anyways, The ViewModel has a button that is bound to a command that runs this code:

repo.ExtractAllZipArchiveSets(
                        ( s , e ) => 
 ... );

... which is a Repository that executes the grunt code:

 public void ExtractAllZipArchiveSets( Action<ObservableCollection<Publication> 
 , Exception> callback , string rootDirectory ) {
.
.
  DirectoryInfo dirInfo = new DirectoryInfo( rootDirectory );
        BackgroundWorker worker = null;

 Thread.Sleep( 2000 );
                            worker = new BackgroundWorker();
                            worker.DoWork += new DoWorkEventHandler( ExtractZipFile );
                            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler( AddPublicationToBeProcessed );
                            worker.ProgressChanged += ProgressChanged;
                            worker.RunWorkerAsync( zipInfo );
                            worker.WorkerReportsProgress = true;
                            worker.ReportProgress( (dirCounter +=1 ) * 10);
.
.
  }

  public void ProgressChanged( object sender , ProgressChangedEventArgs e )
    {
        //ConverterEntity entity = new ConverterEntity();
        //entity.ProgressValue = e.ProgressPercentage;
    }

 private void AddPublicationToBeProcessed(object sender, RunWorkerCompletedEventArgs e)
    {

    }
    private void ExtractZipFile( object sender , DoWorkEventArgs e )
    {  
            FileInfo fileInfo = new FileInfo( ( e.Argument as FileSystemInfo ).FullName );
            DiskFolder destinationFolder = new DiskFolder( fileInfo.Directory.ToString() );
            DiskFile zipFile = new DiskFile( fileInfo.FullName );
            ZipArchive zip = new ZipArchive( zipFile );
            zip.CopyFilesTo( destinationFolder , true , true , "*.r*" );          
    }

... and that's it.

I didn't want my grunt code in the VM, as I just want to call and execute Action methods from it. The Repository seems like the proper place to abstract away all this work.

Probably some kind of DependencyObject would do the trick, as opposed the instanced, and private field used like I have it which clearly won't work.

However, if I want to provide updates to the ProgressControl, how would I do that?

Thank You.

1 Answers1

1

You seem to be missing some information, either on how the BackgroundWorker class works, or how calling methods from external projects... neither of these things would stop you from obtaining your goal.

First, let's make sure that you know how to set up a BackgroundWorker. Rather than repeat myself here, please refer to the answer that I gave to the Progress Bar update from Background worker stalling question, here on StackOverflow. From that example, here is where the ProgressBar.Value property gets updated:

private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar.Value = e.ProgressPercentage;
}

In your case, if you have data bound the ProgressValue property to the ProgressBar.Value property in the UI, then you'd do this:

private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    ProgressValue = e.ProgressPercentage;
}

So far, so good? Ok, so now to 'wire up' your BackgroundWorker from another assembly. Please note that as long as you have an instance of your object, then it really doesn't matter where it was declared. From my example, here is where I hook up the BackgroundWorker:

public TestView()
{
    InitializeComponent();
    backgroundWorker.WorkerReportsProgress = true;
    backgroundWorker.ProgressChanged += ProgressChanged;
    backgroundWorker.DoWork += DoWork;
}

The only difference is that you would need to expose your BackgroundWorker as a property from the other assembly... you'd then use it something like this:

public TestView()
{
    InitializeComponent();
    classInOtherAssemblyReference.BackgroundWorker.WorkerReportsProgress = true;
    classInOtherAssemblyReference.BackgroundWorker.ProgressChanged += ProgressChanged;
    classInOtherAssemblyReference.BackgroundWorker.DoWork += DoWork;
}

To clarify this, you'd need an instance from a class from the other assembly (classInOtherAssemblyReference) in your UI code behind/view model and that class should have a BackgroundWorker property of type BackgroundWorker.

Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183