I do not know how to accurately formulate the question.

There is a function that performs some action. It is located in the "class library". I connect this library to my application. I run this function and want the ProgressBar display the progress of this procedure from the library. How to implement this correctly in the MVVM approach?

UPD: Here is an example of the Vlad method: Function in the library:

 using System; namespace ClassLib { public static class Service { public static void Foo(IProgress<int> progress) { progress.Report(1); string s; for (int i = 1; i <= 99; i++) { for (int j = 0; j <= 50000; j++) s = j.ToString(); progress.Report(i); } progress.Report(100); } } } 

ViewModel:

 public class MainWindowViewModel: INotifyPropertyChanged { private int _progr; public int Progress { get { return _progr; } set { _progr = value;OnPropertyChanged("Progress"); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); public void StartFoo() { Service.Foo(new Progress<int>(persent => Progress = persent)); } } 

View: XAML

 <Window.DataContext> <vm:MainWindowViewModel x:Name="windowVM"/> </Window.DataContext> ... <ProgressBar Value="{Binding Progress, UpdateSourceTrigger=PropertyChanged}"/> 

c # code:

 private void button_Click(object sender, RoutedEventArgs e) { MainWindowViewModel vm = (MainWindowViewModel)this.DataContext; vm.StartFoo(); } 

The process is displayed as follows: 0 pause 100%.

  • Completed the answer. - Vlad

1 answer 1

I would advise the following:

1. In the library method, add an IProgress type parameter .

 public class Service { public void Foo(IProgress<int> progress) { progress.Report(1); DoSomeWork(); progress.Report(100); } } 

2. In the view model, add the Progress property and the progress change handler.

 public class ViewModel { public int Progress { get; set; } // при установке значения вызывает PropertyChanged private void HandleProgressChanged(int progress) { Progress = progress; } private void StartFoo() { Service.Foo(new Progress<int>(HandleProgressChanged)); } } 

3. In the view, bind to the Progress property.

 <ProgressBar Progress="{Binding Progress, UpdateSourceTrigger=PropertyChanged}"/> 

UPD

Well yes. I have simplified the example too much. Your code is synchronous. It runs in a UI thread. Therefore, the window interface hangs. It needs to be run asynchronously: make the library method async or run it via Task.Run. For example:

 public static async Task Foo(IProgress<int> progress) { progress.Report(1); string s; for (int i = 1; i <= 99; i++) { for (int j = 0; j <= 50000; j++) s = j.ToString(); await Task.Delay(1);// это просто имитация бурной деятельности progress.Report(i); } progress.Report(100); } public async Task StartFoo() { await Service.Foo(new Progress<int>(persent => Progress = persent)); } private async void button_Click(object sender, RoutedEventArgs e) { MainWindowViewModel vm = (MainWindowViewModel)this.DataContext; await vm.StartFoo(); } 

I would also advise using commands, rather than button handlers.

  • one
    As an added plus, Progress<Т> delivers messages in the UI stream, no matter in which stream they were sent. - VladD
  • Does not display the process. Immediately 0 pause 100. - MaximK
  • @MaximK, add code to the question, or something. - Vlad
  • At the expense of the button, I also simplified =) - MaximK
  • @MaximK, ok :) Does that work? By the way, here is a good cheat sheet for creating asynchronous commands with a description of a rake that can be stepped on. - Vlad