0

I have a ProgressBar in my MVVM View, which is bound to a View Model property. Updating the property in the VM all works correctly. However, I have some longer-running file/network operations which take place in another class (Model), and I would like to update the ProgressBar property in the middle of the Model operations. I can't pass the ProgressBar property via reference to the Model class. I definitely don't want to pass a handle to the VM to the Model. How do I update this VM property from the Model classes adData and fileOps?

Edit: Additional code to show where I need to update the ProgressBar property.

View

<ProgressBar Value="{Binding ProgressMeter}"/>
<TextBlock Text="{Binding CurrentStatusMsg}"/>

ViewModel

public class ViewModel : INotifyPropertyChanged
{
    private readonly IADData adData;
    private readonly IFileOps fileOps;

    public ViewModel(IADData adData, IFileOps fileOps)
    {
        this.adData = adData;
        this.fileOps = fileOps;
    }

    // INPC Implementation goes here

    private int progressMeter;

    public int ProgressMeter
    {
        get => progressMeter;

        set
        {
            if (progressMeter != value)
            {
                progressMeter = value;
                RaisePropertyChanged("ProgressMeter");
            }
        }
    }

    // Similar property for CurrentStatusMsg

    public void DoIt()
    {
        BackgroundWorker bgWorker = new BackgroundWorker
        {
            WorkerReportsProgress = true
        };
        bgWorker.DoWork += CreatePhoneList;
        bgWorker.RunWorkerCompleted += BgWorker_RunWorkerCompleted;

        CurrentStatusMsg = "Creating Phone List...";

        ProgressMeter = 5;
        bgWorker.RunWorkerAsync();
    }


    private void CreatePhoneList(object sender, DoWorkEventArgs e)
    {
        // How do I update ProgressMeter in adData and fileOps classes?
        DataTable t = adData.ReportLines();
        fileOps.AddDeptRows(t);
        e.Result = t;
    }


    private void BgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        ProgressMeter = 100;
        CurrentStatusMsg = "Creating Phone List...  Complete.";
        reportCreator.ShowReport((DataTable)e.Result);
    }
}
Conrad
  • 2,197
  • 28
  • 53
  • Does your model implement IPNC, are you updating the model property on ui thread? – Xiaoy312 Sep 04 '19 at 18:53
  • @Xiaoy312 I assume you mean `INotifyPropertyChanged`? If so, yes. – Conrad Sep 04 '19 at 18:55
  • Does your view-model expose your model somehow, so that the view can bind to? You should probably post code that shows how your MVVM is wired up and where/how you are updating the said property. – Xiaoy312 Sep 04 '19 at 18:59
  • @Xiaoy312 sorry should have mentioned - I am using Ninject to wire the Model to the VM. That is happening in App.xaml.cs. – Conrad Sep 04 '19 at 19:02
  • At best, your question is too broad. See marked duplicate for general approach. If you are trying to be dogmatic about the relationship between view, view-model, and model, you'll need to delegate: the model will need to implement a progress property with change notification (e.g. `INotifyPropertyChanged`), or you can pass an `IProgress` to it, or any other suitable mechanism to allow a client (like the view-model) to receive progress notification without creating a dependency on the client itself in the model class. – Peter Duniho Sep 04 '19 at 19:23
  • See also https://stackoverflow.com/search?q=mvvm+update+progressbar and related searches – Peter Duniho Sep 04 '19 at 19:24
  • @PeterDuniho please see the edits. I am trying to do more than a general implementation - I need to update the `ProgressBar` outside of the VM. – Conrad Sep 04 '19 at 19:52
  • The commented question in your code is asking the wrong thing: _"How do I update ProgressMeter in adData and fileOps classes?"_ -- you don't. Those classes shouldn't know about the progress bar, just as the view model shouldn't know about it either. As I've already noted above, you should use an existing mechanism to allow those classes to report progress in a general-purpose way, and then your view model can subscribe to that mechanism to update from the background worker. – Peter Duniho Sep 04 '19 at 20:36
  • @PeterDuniho "you should use an existing mechanism to allow those classes to report progress..." - yes, that is what I don't know how to do. This is what I am asking. – Conrad Sep 04 '19 at 20:55
  • 1
    `PubSubEvent` (Prism Lib) or equivalent messaging or events would be your friend in this case. – Adam Vincent Sep 05 '19 at 04:49

1 Answers1

-1

Thanks to @Adam Vincent, I ended up using the Messenger Pattern - specifically this implementation. I can now pass the ProgressBar value from the Model to the View, without breaking abstraction layers.

Conrad
  • 2,197
  • 28
  • 53