Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MVVM PRISM: ProgressBar Interaction Request with Asynchronous Command

0.00/5 (No votes)
25 Sep 2011 1  
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:

<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.

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.

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.

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here