MVVM ProgressBar

There are a lot of options for doing a ProgressBar in WPF using MVVM you can Google it and see for yourself. In this article I'll show a hybrid way of doing MVVM and having a ProgressBar that does not conform to the MVVM premiss (but it is really simple to use).

 The idea is that you can do all the things you are used to do with MVVM but when you get to the ProgressBar you use events and a little code-behind.

Here is the code for the ViewModel:

public class MainWindowViewModel
{
    private BackgroundWorker _backgroundWorker;

    public event EventHandler TaskStarting = (s,e) => { };

    public event ProgressChangedEventHandler ProgressChanged
    {
        add { _backgroundWorker.ProgressChanged += value; }
        remove { _backgroundWorker.ProgressChanged -= value; }
    }

    public event RunWorkerCompletedEventHandler TaskCompleted
    {
        add { _backgroundWorker.RunWorkerCompleted += value; }
        remove { _backgroundWorker.RunWorkerCompleted -= value; }
    }

    private ICommand _executeLongTask;

    public ICommand ExecuteLongTask
    {
        get
        {
            if (_executeLongTask == null)
            {
                _executeLongTask = new RelayCommand(param => _backgroundWorker.RunWorkerAsync());
            }
            return _executeLongTask;
        }
    }

    public MainWindowViewModel()
    {
        _backgroundWorker = new BackgroundWorker();
        _backgroundWorker.WorkerReportsProgress = true;
        _backgroundWorker.DoWork += executeTask;
    }

    private void executeTask(object sender, DoWorkEventArgs e)
    {
        OnTaskStarting();
        for (int i = 0; i < 100; i++)
        {
            Thread.Sleep(100);
            _backgroundWorker.ReportProgress(i + 1);
        }
    }

    private void OnTaskStarting()
    {
        TaskStarting(this, EventArgs.Empty);
    }
}

 This ViewModel class has three events: TaskStartiing, ProgressChanged and TaskCompleted. The last two are just events that I exposed from the BackgroundWorker that will execute my long runing task. The code-behind for the Window will subscribe to these events:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        MainWindowViewModel vm = new MainWindowViewModel();
        vm.TaskStarting += TaskStarted;
        vm.ProgressChanged += ProgressChanged;
        vm.TaskCompleted += TaskCompleted;

        DataContext = vm;
    }

    void TaskStarted(object sender, EventArgs e)
    {
        this.Dispatcher.Invoke(new Action(() => ProgressPopup.IsOpen = true));
    }

    void TaskCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        this.Dispatcher.Invoke(new Action(() => ProgressPopup.IsOpen = false));
    }

    void ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
    {
        this.Dispatcher.Invoke(new Action(() => ProgressBar.Value = e.ProgressPercentage));
    }
}

Notice that since my long running task is being executed in another thread I need to use the Dispatcher to update any user interface elements.

My Window has a Popup with a ProgressBar inside it. I use the Starting and Completed events to show and hide the Popup. The ProgressBar itself is only updated in the ProgessChanged event. I also added an animation to show a blinking progress message during the execution.

<Window x:Class="MvvmProgressBar.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="400">
    <StackPanel>
        <Button Command="{Binding ExecuteLongTask}">Run Long Task</Button>
        <Popup Name="ProgressPopup" 
               Placement="Center" 
               Width="300"
               IsOpen="False">
            <Border BorderThickness="10"
                    BorderBrush="Black"
                    Background="Gray"
                    Padding="30,50">
                <StackPanel>
                    <TextBlock Foreground="White"
                           FontWeight="Bold"
                           FontSize="16"
                           Name="txt"
                           Text="Processing...">
                    <TextBlock.Triggers>
                        <EventTrigger RoutedEvent="TextBlock.Loaded">
               <BeginStoryboard>
                  <Storyboard>
                     <DoubleAnimation
                        AutoReverse="True"
                        Duration="0:0:1"
                        From="1.0"
                        RepeatBehavior="Forever"
                        Storyboard.TargetName="txt"
                        Storyboard.TargetProperty="Opacity"
                        To="0.0"/>
                  </Storyboard>
               </BeginStoryboard>
            </EventTrigger>
                    </TextBlock.Triggers>
                    </TextBlock>
                    <ProgressBar Name="ProgressBar"
                                 Height="30" 
                                 BorderThickness="2" />
                </StackPanel>
            </Border>
        </Popup>
    </StackPanel>
</Window>

This example is really simple but it goes to show you that you may, from time to time, do some non-MVVM code and it won't make your app suck.

 

Comments

Posted by: Madhu
On: 3/1/2010 2:38:22 PM

This is really good post,

here I have a requirement that, when ever I click on Login Button, it will fetch and match the login details and then If it valid then it will navigate to the other windows, so here what i need is that when ever I click on login button, then progress shows that lgoin in processing till it validates and once it is done, then it will move to further navigations, please help me to create the same in MVVM

Leave your comment

Author

Email (never displayed)

Website

Comment  
HTML is NOT allowed. Use regular line breaks and those will be respected.