Sunday, December 9, 2012

Five Months In: Working with Prism in WPF

Several months ago, I put down some initial thoughts regarding Prism: First Impressions: Working with Prism in WPF.  Now that I've been working with Prism for five months, I thought it was time for a follow-up.

It turns out that my opinions haven't changed much from those first impressions.  Prism is working out very well for the project I'm working on.  Taking advantage of the modularity (and the independence of each module) has allowed us to quickly modify and add to our task workflows.  We've managed to keep good separation and isolation which has made unit testing much easier.  The navigation objects are working as expected.  And the helper objects make the code much more compact, readable, and maintainable.

Prerequisites
I have confirmed my opinion that Prism is not for beginning programmers; there are a number of intermediate topics that are necessary to understand.  What is interesting is that I have articles or presentations that are centered around these prerequisites.  I didn't plan for this, it just turns out that the topics I've written about happen to revolve around the foundational knowledge required for Prism.

Here's the list from the previous article along with links to my materials:
These articles are a good place to get started.  By understanding these topics, you will be better prepared to work with Prism in your applications.

Useful Features
In the previous article, I mentioned a number of features that I liked.  That list is still accurate.  I'll go into a little more detail on 2 of these features.

DelegateCommand
The DelegateCommand allows you to create an ICommand object with much less code.  Commanding is used to data bind to buttons in the UI.  Let's compare some code.

To implement ICommand yourself is not very complicated.  But there is a lot of boiler-plate code in a custom object.  Here's a sample command implementation (this is taken from the Dependency Injection session samples):


As we can see, there is quite a bit of code here, but we really only care about the last line (that assigns to the People property of the ViewModel).  The rest is plumbing.

Compare this to using a DelegateCommand from the Prism framework:


This allows us to do the same thing (assign a value to the People property), but we can do this with just one line of code (the DelegateCommand constructor) instead of needing to create a custom class.  The parameter of the DelegateCommand constructor is the "Execute" we want to perform ("Execute" is part of the ICommand interface).

ICommand has another method: "CanExecute".  This returns true or false depending on whether the command can be executed.  In our example, this always returns "true".  But sometimes this is more complicated -- such as if we need to check a property or run some code.  The DelegateCommand constructor takes a second parameter which is a delegate for "CanExecute".  If you do not provide one, then "CanExecute" is always true.

As you can see, DelegateCommand makes our code much more compact.  And if we don't want to use a lambda expression (or if our Execute is several lines of code), then we can use a standard delegate method instead.  The result is that it is easier to see the intent of our code.  Instead of looking through a custom command object for the Execute method, we have the code right here in the constructor (or a pointer to it in the case of a named delegate).

When you have a number of commands in the application, this makes a huge difference in the amount of code that needs to be written as well as the readability of that code.

NotificationObject
NotificationObject is another class that is provided by the Prism framework.  This class implements the INotifyPropertyChanged interface.  This interface needs to be implemented in order for data binding to work as expected in XAML.  The interface itself is not hard to implement (just a few lines of boiler-plate code).  The difference between NotificationObject and manual implementation has to do with the RaisePropertyChanged methods that are available.

When implementing INotifyPropertyChanged, you generally make a call to RaisePropertyChanged in the setter for a Property, such as this:


The RaisePropertyChanged method will alert the UI that the underlying property has been updated and that the UI should refresh the binding.  Notice that this version of RaisePropertyChanged takes a string parameter.

In contrast, NotificationObject provides a RaisePropertyChanged method that takes an Expression.  This lets us write code like this:

Your first thought might be, "I don't see much of a difference."  But there is.  Instead of using a string, we use an expression with the actual Property.  This means that we get IntelliSense when we are creating this method.  It also means we will get a compiler error if we type the property name incorrectly.  And most importantly, if we rename our property, our refactoring tools will automatically update the RaisePropertyChanged method as well.  Using properties over strings is definitely the way to go.

Nothing You Can't Do Yourself
I had an interesting conversation with someone about Prism.  He asked me what I liked about it, and I told him that I really liked the DelegateCommand.  His response was that you could create a DelegateCommand object yourself -- it's only about 15 lines of code.  And he is absolutely correct.

Prism doesn't perform any magic.  It consists of a set of helper classes and methods that you could implement yourself.  But we don't have to.  The Patterns & Practices team has implemented these for us and put them together in a package that is easy to use.

If you don't like any of the implementations, you are free to change them.  Prism comes with all of the source code (including unit tests).  So you can enhance or re-implement any of the features.  My experience is that the features are well-implemented and have met my needs.

What About Windows 8?
My one disappointment around Prism is that it looks like there won't be an implementation for Windows Store Applications.  Prism covers a lot of platforms: WPF, Silverlight 5, and Windows Phone 7.5.  The latest rumors are that there will be a different guidance package from the Patterns & Practices team for Windows Store Applications, and I have not been able to hunt down a beta or a release date at this point.

If you are developing Windows 8 desktop application with WPF, then Prism will work just fine.  But for Windows Store Applications, we will need to wait for another solution.

[UPDATE: There has been announcement regarding guidance for Windows Store Apps.  More info: Update on Windows 8 Guidance]

Wrap Up
As I mentioned, my opinion of Prism has not changed much from my initial impressions of working with it.  It has been a very good framework for my current project, and I will keep it in mind for future projects.  If you are working on fairly straight-forward applications or applications with just a few screens, then Prism may be more than you need.  But if you are working on modular or enterprise-level applications, it may be just what the doctor ordered.

Happy Coding!

No comments:

Post a Comment