Click here to Skip to main content
15,880,392 members
Articles / Desktop Programming / WPF

MVVM PRISM: ProgressBar Interaction Request with Asynchronous Command

Rate me:
Please Sign up or sign in to vote.
4.81/5 (13 votes)
25 Sep 2011CPOL3 min read 48.7K   2.2K   45   6
How to report progress of an asynchronous command by using a custom Interaction Request with a progress bar
Interaction.png

Introduction

I’m pretty sure that if you have been working on a project by using PRISM and MVVM pattern, at some point you have got to deal with Interaction Requests either to present some data or to simulate an interaction like it was done in WinForms with the MessageBox.

Interaction Request can be a bit confusing if you are facing it for the first time. Furthermore, there’s a limited amount of information about it, even not much for WPF desktop version. That is why I decided to post this article. I hope this is going to be useful at least to open the field a bit and show that Interaction Request can be a very powerful resource in your application.

This article pretends to show how you can use a custom Interaction Request Dialog that holds a progress bar which is bound to asynchronous command that does some stuff in the background. The dialog reports the progress and allows the user to cancel the BackgroundWorker that is performing the job. Once the thread is done, the dialog automatically closes itself.

In this article, I assume that you are familiar with MVVM pattern and WPF PRISM.

Understanding the Parts

PRISM allows you to interact with the GUI from the View Model by using InteractionRequest<T>. For this article, I have used InteractionRequest<Notification> because I just want to show some data and cancel an operation. From our View Model, we can raise a Notification to the GUI’s thread in order to be caught by a trigger. Once the trigger has been launched, it invokes a TriggerAction<T> which is the handler to create and manipulate de dialog. In this case, it is a TriggerAction<Grid> due to the trigger definition's hold by a Grid.

The following code example shows how the MainWindow’s XAML looks like:

XML
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>

    <Button Content="Sync Command" Command="{Binding Path=CommonCommand}" Grid.Row="0" />
    <Button Content="Async Command" Command="{Binding Path=AsyncCommand}" Grid.Row="1" />

    <i:Interaction.Triggers>

        <prism:InteractionRequestTrigger SourceObject="{Binding NotificationToProgress}">
            <interactions:InteractionProgressDialog>
                <interactions:InteractionProgressDialog.Dialog>
                    <interactionRequest:ProgressbarInteractionDialog />
                </interactions:InteractionProgressDialog.Dialog>
            </interactions:InteractionProgressDialog>
        </prism:InteractionRequestTrigger>

        <prism:InteractionRequestTrigger SourceObject="{Binding NotificationToClose}">
            <interactions:InteractionCloseDialog>
            </interactions:InteractionCloseDialog>
        </prism:InteractionRequestTrigger>

    </i:Interaction.Triggers>
</Grid>

The interaction dialog has been designed by using MVVM pattern but adding a new feature in order to interact with its View Model from the View and achieving a data communication by using property binding. If you are familiar with MVVM, you have probably heard talk about adapters. An adapter is just another layer between the View and ViewModel with a clear interface to communicate from one place to another for achieving binding.

The following code example shows the Adapter and View Model’s interface.

C#
public interface IProgressbarView
{
    void SetProggessStep(int step);
    void SetProgressMessage(string message);
    void SetTitle(string title);
}

public interface IProgressbarViewModel : Views.IProgressbarView, INotifyPropertyChanged
{
    int Step { get; set; }
    string Message { get; }
    string Title { get; }
}

public interface IProgressbarAdapter : Views.IProgressbarView
{
    IProgressbarViewModel ViewModel { get; }
}

So what have we got…
First of all, we got a Command with a BackgroundWorker that does all the dirty job. That Command implements INotifyPropertyChanged and by using it, it will report the progress to the main View Model.
Once the main View Model has caught a progress notification from the AsynCmd, it will raise a Notification to the View by using InteractionRequest.

C#
void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case IS_BUSY_PROPERTY:
            AsynCmd cmd = this.AsyncCommand as AsynCmd;
            if (!cmd.IsCancelPending)
            {
                this.PropertyChanged(this, 
			new PropertyChangedEventArgs(IS_EXECUTING_PROPERTY));
                if (cmd.IsBusy)
                    App.Current.Dispatcher.Invoke
			(new InteractionDelegate(this.ShowInteraction));
                else
                    App.Current.Dispatcher.Invoke
			(new InteractionDelegate(this.CloseInteraction));
            }
            break;
        case PROGRESS_PROPERTY:
            App.Current.Dispatcher.Invoke(new InteractionDelegate(this.SetProgressStep));
            break;
    }
}

private void ShowInteraction()
{
    (this.NotificationToProgress as ProgressRequest)
        .ShowDialog(TITLE_DIALOG, INITIAL_MESSAGE_DIALOG, this.CancelProcess);
}

private void CloseInteraction()
{
    (this.NotificationToClose as CloseRequest).Close();
}

private void SetProgressStep()
{
    (this.NotificationToProgress as ProgressRequest)
        .SetProgressStep(this.Progress,
        string.Format(PROGRESS_MESSAGE_DIALOG, this.Progress));
}

private void CancelProcess(Notification notification)
{
    (this.AsyncCommand as AsynCmd).CancelProcess();
}

Then the view’s trigger will invoke InteractionProgressDialog which in the first call will creates the dialog’s view and will add the view as a control to the control collection of its container.
For the next notifications, InteractionProgressDialog will set dialog’s View Model’s properties by using the adapter.

C#
protected override void Invoke(object parameter)
{
    var args = parameter as InteractionRequestedEventArgs;
    if (args != null)
    {
        Notification notification = args.Context;
        UIElement element = InteractionDialogBase.FindDialog(this.AssociatedObject);
        this.SetDialog(notification, args.Callback, element);
    }
}

private void SetDialog
	(Notification notification, Action callback, UIElement element)
{
    ProgressRequest.ProgressMessage msg =
    notification.Content as ProgressRequest.ProgressMessage;
    if (this.Dialog is IProgressbarView)
    {
        IProgressbarView view = (IProgressbarView)this.Dialog;
        view.SetProggessStep(msg.Step);
        view.SetProgressMessage(msg.Message);
        if (msg.Title != null)
            view.SetTitle(msg.Title);
    }
    if (element == null)
    {
        EventHandler handler = null;
        handler = (s, e) =>
        {
            this.Dialog.Closed -= handler;
            this.AssociatedObject.Children.Remove(this.Dialog);
            callback();
        };

        if (msg.Initialize)
        {
            this.Dialog.Closed += handler;
            this.Dialog.SetValue(Grid.RowSpanProperty,
            this.AssociatedObject.RowDefinitions.Count == 0 ? 1 :
            this.AssociatedObject.RowDefinitions.Count);
            this.Dialog.SetValue(Grid.ColumnSpanProperty,
            this.AssociatedObject.ColumnDefinitions.Count == 0 ? 1 :
            this.AssociatedObject.ColumnDefinitions.Count);
            this.AssociatedObject.Children.Add(this.Dialog);
        }
    }
}

Once the AsynCmd has completed its work, the main View Model will report the view that everything is done by using NotificationToClose. That notification will be caught by another view’s trigger that uses another handler (InteractionCloseDialog) to close the dialog.

Some Thoughts

In this article, I talked about an interesting way to achieve modal views in MVVM, however I ‘m not saying that this is the proper way to do it. I just posted this article because I know that InteractionRequest is a slightly confusing part of PRISM and because it did make me crazy for a while.

Another way to achieve the same result could be done by using a hidden dialog into the main View and focusing all binding to the main’s View Model like it is done in HTML with JavaScript and divs.

I will be glad to get feedback about it, so feel free to write a comment.

History

  • 09/25/2011 - 1st version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Spain Spain
http://www.linkedin.com/in/gerard-castello-viader
https://github.com/gcastellov

Comments and Discussions

 
QuestionShow progressbar on the same window at bottom Pin
Learning WIX18-Oct-13 22:46
Learning WIX18-Oct-13 22:46 
Generalcool!!! Pin
vikram shantharaj24-Jul-12 10:11
vikram shantharaj24-Jul-12 10:11 
QuestionI like it Pin
BITA Moin26-Apr-12 6:12
BITA Moin26-Apr-12 6:12 
QuestionDone! Pin
poi1191-Oct-11 3:36
poi1191-Oct-11 3:36 
QuestionMy vote of 5 Pin
Reiss27-Sep-11 4:00
professionalReiss27-Sep-11 4:00 
QuestionNice Pin
Sacha Barber27-Sep-11 0:49
Sacha Barber27-Sep-11 0:49 
I like the article, though I have to say I am not massive fan of the way PRISM does Dialogs. I prefer the service approach. Still 5 from me good article
Sacha Barber
  • Microsoft Visual C# MVP 2008-2011
  • Codeproject MVP 2008-2011
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.