WPF: MVVM & Responsive UI

Deutsche Version

A few days ago I wrote about my first tumbling steps into the world of WPF & MVVM.

Now a friend of mine – who’s been working in that very field – has challlenged me:

a) My GUI should remain responsive while the merge is being executed.

b) I should not re-evaluate the entire GUi after every single user action.

These two challenges have very real-world reasons. The user doesn’t like to have the feeling the program hung up. That’s why you’re supposed to write an UI that tells the user: “No, I did not crash, I’m just not done yet!”.

And the second one is something that’s just not noticable on small demo apps such as this one but becomes a problem with bigger GUI: The evaluation of every control after every action can cost a lot of time and performance.

Both can be summarized under the acronym UX which stands for “User eXperience”.

OK, it’s relatively easy to solve challenge a) with the Task Library that comes with .Net Framework 4.5. And even before that you would have simply used a BackgroundWorker thread.gelöst.

But once you moved a job into a background thread you suddenly have a problem that wasn’t there before: The user can suddenly continue to use the program and click the same button twice before the first task is completed.

Now you have to think about what the user is allowed to do while the task is still running. Fortunately, in my case that’s really simple: Nothing. The UI shall just not appear frozen while the task is working.

To deliberately prolong the running time of the task, I added a sleep of 5 seconds in the model so one can actually see something happening. Otherwise the mode stays the same in comparison to the first implementation.

In the ZIP file linked below you’ll find the code change to use a Task in MVVM_responsive.

Opposed to many other examples you’ll find on the internet, you shouldn’t use Task.Factory.StartNew() unless you have really good reasons for it. Just use Task.Run() instead. “>Here is explained why.

You also need to consider the fact that we’re now working in a multi-threaded environment. So we should carefully encapsulate our access to the UI. Thankfully we already interface’d all accesses in the previous implementation. Check out the changes in UIServicesWPF.cs.

But now for part b) of the challenge.

If you steer away from the global CommandManager you’re once again responsible to determine dependencies between controls and their values and states and to update them.

In my case the merge button shall only be active if the text boxes are filled in. I.e the state of Button.Enabled is dependent on the value of TextBox.Text.

In MVVM_NoRequery and MVVM_NoRequery_alternate I’ve shown two ways to do it.

As always, the complexity depends on how many controls are dependent on each other in how many ways.

The solution for bigger and more complex ViewModels is shown on StackOverflow.