Do You DoEvents?

Often times during the development of enterprise software requirements arise stating that specific operations need to be handled both synchronously and asynchronously. A perfect example of this is printing. Perhaps a user wishes to print a document and wait for confirmation of a successful print operation before moving on to the next step of their business process. Or perhaps the user wants to request a print operation for multiple documents in a fire and forget fashion. In one case we should provide some sort of user interface cue as to the status of the print operation and in the other we just fire and forget, while hoping everything works out.

Thinking Synchronously

So we have a WPF window with a Print Button we also have a label to indicate the status of the running print job.

Some XAML

<Window x:Class="WpfApplication2.Print"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Print" Height="300" Width="300">
 <Grid>
 <StackPanel>
 <Button Command="{Binding Path=PrintCommand}">Print</Button>
 <Label Content="{Binding Path=PrintStatus}"/>
 </StackPanel>
 </Grid>
</Window>

Code Behind

using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace WpfApplication2
{
    /// <summary>
    /// Interaction logic for Print.xaml
    /// </summary>
    public partial class Print : Window, INotifyPropertyChanged
    {
        #region Private Data Members

        private string mPrintStatus;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="Print"/> class.
        /// </summary>
        public Print()
        {
            InitializeComponent();
            DataContext = this;
            PrintCommand = new RelayCommand(PrintExecuted);
            PrintStatus = "Ready";
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Prints the specified parameter.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        private void PrintExecuted(object parameter)
        {
            PrintStatus = "Print Executed";
            //Long Running Print Operatoin
            Thread.Sleep(1000);
            PrintStatus = "Print Completed";
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the print status.
        /// </summary>
        /// <value>The print status.</value>
        public string PrintStatus
        {
            get
            {
                return mPrintStatus;
            }
            private set
            {
                mPrintStatus = value;

                var handler = PropertyChanged;
                if (handler != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("PrintStatus"));
                }
            }
        }

        /// <summary>
        /// Gets or sets the print command.
        /// </summary>
        /// <value>The print command.</value>
        public RelayCommand PrintCommand
        {
            get;
            private set;
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}

Looking at the code here you can see that the intended behavior is as follows.

  1. User Clicks Print Button
  2. PrintCommand is executed
  3. A label used to show the print status is updated from ‘Ready’ to ‘Print Executed’
  4. The long running Thread.Sleep is called simulating a synchronous print operation
  5. Once complete the status is again changed to ‘Print Completed’

This sequence of events does actually happen, the problem however is that it all happens within the execution scope of the  PrintExecuted method. Since this method is blocking until completed the user never sees the nice status updates we provide.

Before Click

Before Click

When the window is loaded the status is set to ‘Ready’ as seen in the image on the left. Once the print button is clicked we want to change the status to ‘Print Executed’ and finally when the PrintCommand is complete to ‘Print Complete’. However since the updates to the status all happen while the UI thread is blocking, the status of ‘Print Executed’ never makes it to the user. Instead the status will transition from ‘Ready’ directly to ‘Print Completed’  upon command completion. This presents a pretty serious issue if you need to give the user meaningful feedback about what is going on. On interesting way to get around this is to use DoEvents.

Do a What?

If you came to the wonderful world of WPF DoEvents was something new to you. In fact when I first presented this problem to a colleague with a strong Win Forms background he was quick to mention this. So what is it?

Take a look

To make a long story short DoEvents allows you to interrupt current event processing in order to process waiting events in the message pumps message queue. The good/bad news depending on your view of such things, is that if you need to replicated DoEvents in WPF you can. Because  WPF and WinForms both leverage the User32 message pump. the solution ends up to be very simple Lets take a look and what our PrintExecuted method might look like.

#region Private Methods

        /// <summary>
        /// Prints the specified parameter.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        private void PrintExecuted(object parameter)
        {
            PrintStatus = "Print Executed";
            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));
            Thread.Sleep(1000);
            PrintStatus = "Print Completed";
        }

        #endregion

This approach give us the desired behavior of all of the status updates being presented to the user. Why this may not be a great illustration of proper use of an admitted hack. It is always good to have another tool in your toolbox.

Enjoy!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Advertisements
%d bloggers like this: