www.wpftutorial.net – just found a new WPF Tutorial site
I just found http://www.wpftutorial.net.
Just another resource for WPF information.
Archive for the ‘Architecture’ Category.
I just found http://www.wpftutorial.net.
Just another resource for WPF information.
WPF provides a ProgressBar control. But there isn’t really a manual for it, especially if you want to follow MVVM.
So I am going to make a little application that counts from zero to ten and tracks the progress. You are going to see when it is OK to use the foreground and when it is not OK but better to use BackgroundWorker.
While much of this code may be production ready, you should be aware that this code intentionally implements a foreground process that is an example of what not to do.
There are two basic classes used for MVVM.
These are found on different blogs and different posts all over the internet, so I would say they are public domain, or free and unlicensed.
class ProgressBarViewModel : ViewModelBase { }
This will be populated as we create our View.
Ok, so lets create the GUI.
Here is the XAML.
<Window x:Class="WPFProgressBarUsingBackgroundWorker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPFProgressBarUsingBackgroundWorker" Title="MainWindow" > <Window.Resources> <local:ProgressBarViewModel x:Key="PBVM" /> </Window.Resources> <Grid> <StackPanel> <Label Content="{Binding Path=Value}" DataContext="{StaticResource ResourceKey=PBVM}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Name="labelNumberCounter" VerticalAlignment="Center" FontSize="175" /> <ProgressBar Margin="0,3,0,3" Height="20" Name="progressBar" Value="{Binding Path=Value}" DataContext="{StaticResource ResourceKey=PBVM}" Minimum="{Binding Min}" Maximum="{Binding Max}"/> <Button Command="{Binding Path=IncrementBy1}" Content="Manual Count" DataContext="{StaticResource PBVM}" Height="23" IsEnabled="{Binding Path=IsNotInProgress}" Name="button1" Width="Auto" /> <Button Margin="0,3,0,3" IsEnabled="{Binding Path=IsNotInProgress}" Command="{Binding Path=IncrementAsForegroundProcess}" DataContext="{StaticResource ResourceKey=PBVM}" Content="Count to 10 as a foreground process" HorizontalAlignment="Stretch" Height="23" Name="buttonForeground" VerticalAlignment="Top" Width="Auto" /> <Button Margin="0,3,0,3" IsEnabled="{Binding Path=IsNotInProgress}" Command="{Binding Path=IncrementAsBackgroundProcess}" DataContext="{StaticResource ResourceKey=PBVM}" Content="Count to 10 as a background process" HorizontalAlignment="Stretch" Height="23" Name="buttonBackground" VerticalAlignment="Top" Width="Auto" /> <Button Command="{Binding Path=ResetCounter}" Content="Reset" DataContext="{StaticResource PBVM}" Height="23" IsEnabled="{Binding Path=IsNotInProgress}" Name="buttonReset" Width="Auto" /> </StackPanel> </Grid> </Window>
Here is the code for the ProgressBarViewModel.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading; using System.Text; using System.Windows.Input; using MVVM; namespace WPFProgressBarUsingBackgroundWorker { class ProgressBarViewModel : ViewModelBase { #region Member Fields Double _Value; bool _IsInProgress; int _Min = 0, _Max = 10; #endregion #region Member RelayCommands that implement ICommand RelayCommand _Increment; RelayCommand _IncrementBy1; RelayCommand _IncrementAsBackgroundProcess; RelayCommand _ResetCounter; #endregion #region Constructors /// <summary> /// The default constructor /// </summary> public ProgressBarViewModel() { } #endregion #region Properties /// <summary> /// Used to mark if the counter is in progress so the counter can't be started /// while it is already running. /// </summary> public bool IsInProgress { get { return _IsInProgress; } set { _IsInProgress = value; NotifyPropertyChanged("IsInProgress"); NotifyPropertyChanged("IsNotInProgress"); } } public bool IsNotInProgress { get { return !IsInProgress; } } public int Max { get { return _Max; } set { _Max = value; NotifyPropertyChanged("Max"); } } public int Min { get { return _Min; } set { _Min = value; NotifyPropertyChanged("Min"); } } /// <summary> /// This is the Value. The Counter should display this. /// </summary> public Double Value { get { return _Value; } set { if (value <= _Max) { if (value >= _Min) { _Value = value; } else { _Value = _Min; } } else { _Value = _Max; } NotifyPropertyChanged("Value"); } } #region ICommand Properties /// <summary> /// An ICommand representation of the Increment() function. /// </summary> public ICommand IncrementBy1 { get { if (_IncrementBy1 == null) { _IncrementBy1 = new RelayCommand(param => this.Increment()); } return _IncrementBy1; } } /// <summary> /// An ICommand representation of the IncrementProgressForegroundWorker() function. /// </summary> public ICommand IncrementAsForegroundProcess { get { if (_Increment == null) { _Increment = new RelayCommand(param => this.IncrementProgressForeground()); } return _Increment; } } /// <summary> /// An ICommand representation of the IncrementProgressForeground() function. /// </summary> public ICommand IncrementAsBackgroundProcess { get { if (_IncrementAsBackgroundProcess == null) { _IncrementAsBackgroundProcess = new RelayCommand(param => this.IncrementProgressBackgroundWorker()); } return _IncrementAsBackgroundProcess; } } /// <summary> /// An ICommand representation of the Reset() function. /// </summary> public ICommand ResetCounter { get { if (_ResetCounter == null) { _ResetCounter = new RelayCommand(param => this.Reset()); } return _ResetCounter; } } #endregion ICommand Properties #endregion #region Functions /// <summary> /// This function manually increments the counter by 1 in the foreground. /// Because it only increments by one, the WPF control bound to Value will /// display the new value when this function completes. /// </summary> public void Increment() { // If we are in progress already, don't do anything if (IsInProgress) return; // If the value is already at 10, start the counting over. if (Value == 10) Reset(); Value++; } /// <summary> /// This function starts the counter as a foreground process. /// This doesn't work. It counts to 10 but the UI is not updated /// until the function completes. This is especially problematic /// since the buttons are left enabled. /// </summary> public void IncrementProgressForeground() { // If we are in progress already, don't do anything if (IsInProgress) return; Reset(); IsInProgress = true; Value = 0; for (int i = _Min; i < _Max; i++) { Value++; Thread.Sleep(1000); } IsInProgress = false; } /// <summary> /// This starts the counter as a background process. /// </summary> public void IncrementProgressBackgroundWorker() { // If we are in progress already, don't do anything if (IsInProgress) return; Reset(); IsInProgress = true; BackgroundWorker worker = new BackgroundWorker(); // Configure the function that will run when started worker.DoWork += new DoWorkEventHandler(worker_DoWork); /*The progress reporting is not needed with this implementation and is therefore commented out. However, in your more complex application, you may have a use for for this. //Enable progress and configure the progress function worker.WorkerReportsProgress = true; worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); */ // Configure the function to run when completed worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); // Launch the worker worker.RunWorkerAsync(); } /// <summary> /// This is the function that is called when the worker is launched with the RunWorkerAsync() call. /// </summary> /// <param name="sender">The worker as Object, but it can be cast to a worker.</param> /// <param name="e">The DoWorkEventArgs object.</param> void worker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; for (int i = _Min; i < _Max; i++) { Value++; Thread.Sleep(1000); } } /// <summary> /// This worker_ProgressChanged function is not in use for this project. Thanks to INotifyPropertyChanged, this is /// completely unnecessary. /// </summary> /// <param name="sender">The worker as Object, but it can be cast to a worker.</param> /// <param name="e">The ProgressChangedEventArgs object.</param> void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { // Does nothing yet throw new NotImplementedException(); } /// <summary> /// This worker_RunWorkerCompleted is called when the worker is finished. /// </summary> /// <param name="sender">The worker as Object, but it can be cast to a worker.</param> /// <param name="e">The RunWorkerCompletedEventArgs object.</param> void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { IsInProgress = false; } /// <summary> /// This function resets the Value of the counter to 0. /// </summary> private void Reset() { Value = Min; } #endregion } }
I’m sorry that this is not the most Newbie proof post. But I tried to comment like crazy the code so you can get through it.
Now if you find a discrepancy in my walk-through, please comment. Also, if it is easier for you to just download the project, here it is:
WPFProgressBarUsingBackgroundWorker.zip