
Fig. 1. Loading user list
Introduction
Disclaimer: This article is more concept than production ready tips'n'tricks (though they are here too but not in reliable state). I advice bringing the solution's classes to some adequate state before using them in production.
UPD: seems like the disclaimer misleads - the attached solution is working, ready to be built, and functioning - but what I was trying to say is it's not perfect and the classes need a serious touch to be used in other projects.
I don't like when an application hangs during long operations leaving me in doubts. In the modern era (most of you, dear readers, have two and more cores in your processor), it's just impolite. But on the other hand, being a software developer, I understand that it's quite tricky to make reliable software in a non-blocking style.
Due its architectural legacy Windowstm OS family still has some problems with its UI updates from a "not main" thread. But as time goes on, it's 2011 and we have .NET 4 at our disposal. So let's look at how to achieve our goal.
Go, Go, Go
In the sample download, you will find a VS 2010 solution containing a WPF project with the same name. I strongly recommend downloading it and giving it a try; play with it for a minute. Its idea is simple - we have some set of users and are able to edit it and its elements.
Now look into the code, find a class called UsersProvider
, its methods are:
public User CreateUser()
{
Thread.Sleep(1500); var user = new User();
return user;
}
public void RemoveUser(User usr)
{
Thread.Sleep(1500);}
As you can see, this class is useless and does almost nothing but slows down execution of methods in a thread. Now imagine that the user creation and removing uses some distant Web-Service or some old-fashioned crappy database. :) And it takes a lot of time (not substantial in machine-feel time, but rather in human feelings) to accomplish operations.
We are modern and object-oriented - just like our favorite language, aren't we? So using the MVVM paradigm in the application is just what we need. A lot of mambo-jambo takes place in different parts of the app, but the main logic is kept in MainWindowController
. As you can see, we have a collection Users
(of guess what - right, users) which is populated during controller instantiation:
if (!WPFHelper.IsInDesignMode)
{
var tsk = Task.Factory.StartNew(InitialStart);
tsk.ContinueWith(t => { MessageBox.Show(t.Exception.InnerException.Message); },
CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.FromCurrentSynchronizationContext());
}
That's it - here comes the creation of the Task
- which is continued in the case of emergency (TaskContinuationOptions.OnlyOnFaulted
) in the dispatcher thread ( TaskScheduler.FromCurrentSynchronizationContext()
) by showing an exception message. FYI, you can freely use t.Exception.InnerException
because of the Task
class' nature - AggregateException
as Task.Exception
always pops up when an exception occurs. WPFHelper.IsInDesignMode
is a static
property used only to ensure that we are really running an app (if you like designing in Expression or in the built-in VS visual designer, that'll come in handy - otherwise it's just a politeness for your team members who do like it). That's all for a start - now let's look into the MainWindowController.InitialStart
method:
void InitialStart()
{
try
{
State = StateEnum.Busy;
User user = _provider.CreateUser();
user.FullName = "John Smith";
.. Users.Add(user);
user = _provider.CreateUser();
..
Users.Add(user);
}
finally
{
State = StateEnum.Idle;
}
}
There are two points of interest here - first, I just remembered that in Task.Factory.StartNew, it's better to set TaskScheduler.Default
in other way you're risking to run on dispatcher thread (depending from place where code is called) loosing all the "magic" I'm showing. One day I'll update the sources but now, at 3 a.m. I'm too sleepy to do it. Next - bring your attention to StateEnum
and State
property. They are our link to UI - they help us to give UI a signal: "We are busy at the moment" and our XAML reacts to it in a following way:
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="Busy">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
and on the contrary in case of LoadingAnimation
, control contained in the same column and row in a Grid (I mean opposite Visibility setting). Actually it's better to use VisualStateManager to control visibility when you need complex behavior. But in my case, triggers are OK.
Users
collection itself is not as simple as you can think. ObservableCollection
by default does not allow manipulation from threads other than it was created on. That's why I use DispatcherCollection
- the idea is simple, make all operations on dispatcher thread but start them from any place you wish:
protected override void OnCollectionChanged(
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (Dispatcher != null && !Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke((Action)(() =>
base.OnCollectionChanged(e)), DispatcherPriority);
}
else
{
base.OnCollectionChanged(e);
}
}
After initial loading of users, we want to do something with that list. "Add" and "Remove" are obvious choice of commands - they are both contained in MainWindowController
and bound here:
<Button Style="{StaticResource toolButtonStyle}" Command="{Binding AddUserCommand}">
<StackPanel Orientation="Horizontal">
<Image Width="20" Height="20" Source="Images/add_user.png"/>
<TextBlock Text="Add user"/>
</StackPanel>
</Button>
<Button Style="{StaticResource toolButtonStyle}"
Command="{Binding RemoveUserCommand}">
...
</Button>
ActionCommand
was implemented in a few minutes so it has a few design issues (getting roots in CommandManager
and CanExecute
requery) - but it does what I want, it allows me to get rid of annoying RoutedCommand
s and its bindings. In certain situations, routed commands is a must but my way is quick and clear.

Pic 2. Toolbar button representing ActionCommand
And at the end, take a quick look at PropertyChangedHelper
and a way all properties in controller are defined - I use snippet and recommend to do so to everybody if you value your time and efforts. The structure goes here:
class User:PropertyChangedHelper
{
#region FullNameProperty
public static readonly PropertyChangedEventArgs FullNameArgs =
PropertyChangedHelper.CreateArgs<User>(c => c.FullName);
private string _FullName;
public string FullName
{
get
{
return _FullName;
}
set
{
var oldValue = FullName;
_FullName = value;
if (oldValue != value)
{
OnFullNameChanged(oldValue, value);
OnPropertyChanged(FullNameArgs);
}
}
}
protected virtual void OnFullNameChanged(string oldValue, string newValue)
{
}
#endregion
}
PropertyChangedHelper.CreateArgs
is great in helping you to keep your property name consistent - you don't have to keep an eye on string
literal with your property name anymore, feel free to use automatic rename now.
Many Thanks
I borrowed ideas from a lot of sources. That's why some of the classes are not suitable for production - they are oversimplified (remember just ideas reinterpreted, not ready solution). Exception is LoadingAnimation
control - I borrowed it somewhere here, on CodeProject. Pity, but I can't remember the author - if you do send me I'll give him credit. Few ideas was borrowed from Prizm, few from my work projects and colleagues (Artem Sovetnikov is awesome on WPF). This sum of technologies allows fast and reliable creation of responsive UI - feel free to use if you find it handy!
... That's Not All Folks
There are such thing as Reactive framework - and it deserves another article.