Home > database >  Get loop value from a method in another class
Get loop value from a method in another class

Time:08-17

I created a method to copy files and folders, this method is present in a class called Helper

static void List(string folder)
{
//methods to get files and folders and return IEnumerable

   foreach (var dname in directories)
       Copy(dname);
   foreach (var fname in files)
       Copy(fname);
}

which works fine, but I need to show the user the progress (bar or percentage) when he clicks a button in the MainForm.

so far I have this code and should add Task or similar to get the progress but it needs a loop.

private void btnCopy_Click(object sender, EventArgs e)
{
string dir = "Some\\Path";
Helper.List(dir);
}

Obviously I cannot track what I cannot count so I was advised using delegates to get values from the other classes method, but I am just starting and I have failed to find some useful help online. Any help?

CodePudding user response:

What you should do is add a IProgress<T> parameter to your List-method. That method should then call the .Report(T) method to describe the progress. Your caller should create a Progress<T> object, and attach an event handler or callback to let the UI know when the progress has advanced.

The actual work needs to be done on a background thread, usually done with a Task.Run, like:

private async void btnCopy_Click(object sender, EventArgs e)
{
  string dir = "Some\\Path";
  var progress = new Progress<int>(ProgressCallback);
  try{
    await Task.Run(()=> Helper.List(dir, progress ));
  }
  catch{
  // handle failure
  }
}
private void ProgressCallback(int p){
    // Update UI
}

static void List(string folder, IProgress<int> p)
{
  for(int i = 0; i < directories.Count; i  ){
   p.Report(i);
   ...
  }
}

My personal preference is to create a custom Progress-object, with features like including things like a description string, and allow it to be split to handle nested loops.

CodePudding user response:

Here are the steps as I see it:

  1. Create a progress bar in your view (I'm going to assume you're using WPF)
  2. Find out how many files/directories you'll be copying, and set that to your progress bar max
  3. Each time you copy a file/directory, increment your progress bar current value
  4. Have that change apply to the view

With that written out in English, lets try coding it:

1. Create the progress bar in the xaml

<ProgressBar Name="progressBar" />

2/3. Set the maximum and increment

This is hard because your List method is static, and inside your Helpers

For the time being, I'll write out a method that you can put in your MainWindow.Xaml.cs (or whichever window you're working on), and then you can figure out the best place to change this

private void List(string folder)
{
  //methods to get files and folders and return IEnumerable

  var count = directories.Count()  
    files.Count();
  progressBar.Maximum = count;
  foreach (var dname in directories)
  {
    Copy(dname);
    progressBar.Value  ;
  }
  foreach (var fname in files)
  {
    Copy(fname);
    progressBar.Value  ;
  }
}

4. Make this work on a Thread to prevent the UI from locking

When calling your List, it might be an idea to run that in a task:

private void btnCopy_Click(object sender, EventArgs e)
{
  string dir = "Some\\Path";
  Task t = new Task(() => List(dir));
  t.Start();
}

But with that running in the background, you'll get an error now as you can't update the progress bar anymore. Not unless we send back our commands via the dispatcher

For this, I tend to create a method to make the calls a little easier:

private void UpdateOnView(Action action)
{
  Dispatcher.Invoke(action);
}

Then in use:

private void List(string folder)
{
  //methods to get files and folders and return IEnumerable

  var count = directories.Count()  
    files.Count();
  UpdateOnView(() => { progressBar.Maximum = count; });
  foreach (var dname in directories)
  {
    Copy(dname);
    UpdateOnView(() => { progressBar.Value  ; });
  }
  foreach (var fname in files)
  {
    Copy(fname);
    UpdateOnView(() => { progressBar.Value  ; });
  }
}

If that updates too quickly, and you want to see your progress bar in action, chuck a Thread.Sleep(100); in your loops, and it'll slow things down for ya

CodePudding user response:

Take a look at BackgroundWorker. This allows you to punt that IO work off the main thread and communicate progress to the UI via calling ReportProgress and handling the ProgressChanged event.

Since you are enumerating over the files and directories to copy them, you'll know the total number which you can use to determine the progress percentage as you work through those lists.

  • Related