0

I move files with

foreach(file){
file.move(file,dst);}

I want to update a form after each file was moved (different partitions) with information about progress. Unfortunately my WPF form is busy during entire copy/move queue. I tried to InvalidateVisual(), without success- what can I do to ensure GUI responsiveness during (or after) moving a file?

Kassan
  • 59
  • 4
  • 1
    Do your work in BackgroundWorker, or Async, so you don't block your UI thread. More info here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx – etaiso Nov 03 '13 at 11:38
  • Take a look at this question and answer http://stackoverflow.com/questions/5483565/how-to-use-wpf-background-worker :). – Lukasz M Nov 03 '13 at 11:38
  • Thank you! Do I need separate BackgroundWorker for each move? Or just one worker to make GUI responsive? – Kassan Nov 03 '13 at 11:44
  • Just put the foreach loop inside the background worker DoWork method. You can also report progress after each file and do cancellation checks but your GUI will be fully responsive. – BrutalDev Nov 03 '13 at 12:15

2 Answers2

2

Kasan, I think the BackgroundWorker will be useful for you. Task is nice way but to update UI progress from task you will need to bother with dispatching events to the UI thread, otherwise you will get exception, because it's not allowed to update UI from threads other than UI. Here is a sample and link to the documentation

 public partial class MainWindow : Window
{
    BackgroundWorker _worker = new BackgroundWorker();
    public MainWindow()
    {
        InitializeComponent();
        _worker = new BackgroundWorker();
        _worker.DoWork += worker_DoWork;
        _worker.ProgressChanged += worker_ProgressChanged;
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progress1.Value = e.ProgressPercentage;
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        _worker.RunWorkerAsync();
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        var files = new List<string>();
        foreach(var file in files)
        {
            File.Move(file, /*target*/);
            _worker.ReportProgress(/* here is progress from 0 to 100 */)
        }
    }
}

BackgroundWorker Sample

Oleg
  • 1,100
  • 2
  • 11
  • 16
0

If you would like to keep your GUI responsive you should start a new thread doing this long task.

This way your GUI thread is not going to be blocked by this long operation.

class example
{
    static void UpdateStatus(int i)
    {
        // UpdateStatus
        Console.WriteLine(i + "Done!");
    }

    static void Main(string[] args)
    {
        int state = 0;
        Task t = Task.Run(
              () =>
              {
                  // do your stuff - this is going to run async
                  for (int i = 0; i < 10; i++)
                      file.move(file,dst);
                      UpdateStatus(i); // callback to update status 
                  }
              }
            );
        Console.WriteLine("Started"); // You wont block this code by running the async part
        t.Wait(); 
        Console.WriteLine("DONE");
        Console.ReadKey();

    }
}

In your case you could use like this:

        Task t = Task.Run(
              () =>
              {
                  // do your stuff - this is going to run async
                  foreach(File file in Files){
                  {
                      move(file);
                      UpdateStatus(i); // callback to update status (eg.:how many files have been moved yet)
                  }
              }
            );
Attila
  • 3,206
  • 2
  • 31
  • 44
  • 1
    It's better to confugure TaskScheduler.FromCurrentSynchronizationContext, to avoid exception if UI object is updated in UpdateStatus method. – Oleg Nov 10 '13 at 22:53