-1

I have a number of progress bars each bound to their own int property which updates their progress. Behind the scenes, I have a method that is being run multiple times which does my processing (looping through data) which I use to use to update the property which updates the progress bar.

The problem I'm having is you cannot pass a property to the method. I could create multiple copies of the method, each referring to the specific property per progress bar but this would require a lot of duplicate code. How would one recommend I go about passing the property to the method?

Essentially this is what I am trying to do but obviously passing the property by ref won't work.

Method(fileList, errorList, _ViewModel.ProgressBarProp1);
Method(fileList, errorList, _ViewModel.ProgressBarProp2);
...

private static void Method(IEnumerable<string> fileList, List<Tuple<string, 
    string>> errorList, ref int PropertyInt)
{
    foreach (var file in fileList)
    {
        if (!File.Exists(file))
        {
            errorList.Add(new Tuple<string, string>(file,
                " does not exist in the folder"));
        }

        PropertyInt++;
    }
}

I have seen this question but this deals with Strings and I haven't been able to implement any of the solutions for an Integer scenario.

UPDATE

Implementing Mike's solution below is allowing access to the property from within the method but I'm having some odd behaviour with the way I am running this concurrently.

I'm implementing the code like so, two methods run simultaneously but incrementing the same property value (i.e. both are associated to the same progressbar).

var taskList = new List<Task>
{
    Task.Run(() =>
    {
        Method1(fileList, errorList, p => _ViewModel.ProgressBarProp1 = p);
    }),
    Task.Run(() =>
    {
        Method2(fileList, errorList, p => _ViewModel.ProgressBarProp1 = p);
    })
};
await Task.WhenAll(taskList.ToArray());

However, only one appears to be updating. What would be the reason and is there a work around?

windowsgm
  • 1,566
  • 4
  • 23
  • 55
  • @Clemens Nope, I want to dynamically update the property as the method runs, not update when the method has completed. – windowsgm Jan 30 '18 at 14:36
  • You can pass property and property name as string. Then after you will update property(PropertyInt) you can call PropertyChanged with name that you passed. But your method should run in separate thread, if you want to see update in UI. – Sasha Jan 30 '18 at 15:19

1 Answers1

0

Replace your ref int with an Action<int> delegate. When you make progress, invoke the delegate with the new progress value. At the call site, pass in a lambda that takes the new progress value and assigns it to your property.

Method(fileList, errorList, p => _ViewModel.ProgressBarProp1 = p);

private static void Method(
    IEnumerable<string> fileList,
    List<Tuple<string, string>> errorList,
    Action<int> reportProgress)
{
    var progress = 0;

    foreach (var file in fileList)
    {
        if (!File.Exists(file))
        {
            errorList.Add(new Tuple<string, string>(file,
                " does not exist in the folder"));
        }

        reportProgress?.Invoke(++progress);
    }
}

If you will be responding to progress on the UI thread but reporting it from another thread, you should make sure your callback marshals itself onto the UI thread. You can accomplish this with a simple helper method:

public static class DispatcherHelper
{
    public static Action<T> MakeInvoker<T>(
        this Dispatcher dispatcher,
        Action<T> action,
        DispatcherPriority priority = DispatcherPriority.Normal)
    {
        return o => dispatcher.BeginInvoke(priority, action, o);
    }
}

Then, modify your invocation as follows:

var dispatcher = Application.Current.Dispatcher;
var reportProgress = dispatcher.MakeInvoker<int>(p => _ViewModel.ProgressBarProp1 = p);

await Task.WhenAll(
    Task.Run(() => Method1(fileList, errorList, reportProgress)),
    Task.Run(() => Method2(fileList, errorList, reportProgress)));
Mike Strobel
  • 25,075
  • 57
  • 69
  • Thanks for this, please see my update above. – windowsgm Jan 30 '18 at 16:01
  • Sounds like a simple data race. I updated my answer with some guidance. – Mike Strobel Jan 30 '18 at 16:40
  • To clarify, incrementing a property is not an atomic operation, and if two threads do it concurrently, you will get unpredictable results. – Mike Strobel Jan 30 '18 at 17:21
  • I've created a simplified solution to test this. Add a progressbar, bind maximum to TextMax and Value to TextProgress and you will notice that the progress bar only fills to about half way. Full code here: https://fnpaste.com/bmrE – windowsgm Jan 30 '18 at 17:40