I just finished reading Dependency Injection in .NET by Mark Seemann (Amazon link). This is an excellent description of Dependency Injection (DI), the associated patterns, and several of the main DI frameworks. Seemann has pulled together a wide range of resources (books, magazine articles, blog posts) and created a comprehensive work. It is apparent that this is based on a ton of research and personal experience in the DI world.
This turned out to be a good and timely read for me. A couple months ago, I started working on a WPF project using Prism, and Dependency Injection plays a big role in that. A colleague of mine (and former co-worker) highly recommended this book, and so I picked up a copy. (Disclaimer: I am a "book person"; that is how I learn best. Other people have different learning methods, so feel free to take this from a "book person" perspective.)
Part 1
Part 1 is an overview of Dependency Injection. Seemann describes what DI is, and also dispels some misconceptions about DI. He covers the benefits of DI including late binding, extensibility, parallel development, maintainability, and testability. He also lays a foundation by showing good and bad examples of implementing DI and how each impacts achieving the desired benefits. Finally, he introduces the topics that will be used in the rest of the book, including the design patterns, DI features, and the DI containers that are available.
Part 2
Part 2 covers the patterns and anti-patterns in DI. The patterns include Constructor Injection, Property Injection, Method Injection, and Ambient Context. Seemann does a good job of covering these patterns including both the benefits and the drawbacks of each. It becomes apparent very quickly that he favors Constructor Injection wherever possible and that the other patterns should be used only when Constructor Injection is not possible. (As a side note: I came to this same opinion in my fairly-short time working with DI.)
The anti-patterns include Control Freak, Bastard Injection, Constrained Construction, and Service Locator. Of these, the Service Locator is the most controversial. Many people consider Service Locator to be a valid DI pattern (rather than an anti-pattern). Seemann himself admits that he promoted the use of Service Locator for many years. He eventually came to the point where the shortcomings of the pattern outweighed the benefits. Prism (the Microsoft Patterns and Practices application guidance) has a service locator baked in (to reference either Unity or MEF for DI out-of-the-box). In my experience with Prism, we have used the service locator pattern, and I can see the benefits and shortcomings. At this point in the project, the scale leans towards the benefits, and we are willing to work with the drawbacks.
Part 2 also has a chapter on "DI Refactorings". This is a very practical review of what types of DI situations you'll run across in the real world -- like dealing with cyclic dependencies and short-lived dependencies. These are great topics to cover. Because the DI containers that we normally work with add the layer of abstraction, sometimes we forget about some potential issues (by assuming that the container is dealing with it).
Part 3
Part 3 covers some big DI topics: Object Composition, Object Lifetime, and Interception. Object Composition has to do with how we use DI to compose our objects. Seemann shows examples for how to compose objects with various technologies -- from easy implementations with console and WPF applications, to difficult implementations with ASP.NET applications and PowerShell commandlets.
Object Lifetime has to do with how the dependencies themselves are managed. Do you always return the same instance of a dependency no matter who asks for it (Singleton)? Or do you return a new instance of a dependency each time (Transient)? Or do something inbetween? Seemann covers the various lifetimes and the pros and cons for each. As with everything, we need to consider our particular situation and select the right answer for the task at hand.
Finally, Seemann covers Interception. This is the idea of using the DI container to inject cross-cutting concerns into our application. For example, we could have the container inject logging or error handling into each of our dependencies. This was a very interesting topic to read about. He also covers Aspect Oriented Programming (AOP) and compares/contrasts it with using DI to cover the same concerns.
Part 4
Part 4 covers several DI containers. What I really liked about this section is that Seemann uses the same examples in each chapter. This means that instead of focusing on the example itself, we can focus on the differences in the container implementations. The containers covered are not exhaustive (5 containers), but they are some of the most widely-used ones.
The containers include Castle Windsor, StructureMap, Spring.NET, Autofac, and Unity. Using each container, examples cover configuration, managing lifetime, interception, and how working with difficult APIs (the difficult APIs refers to the dependencies that we are registering/resolving, not the container APIs themselves). Not all containers support all features out-of-the-box, and Seemann shows some possible work-arounds for the features that are not implemented directly by the container. The end result is that most of the containers work pretty much the same (which is good), but there are slight variations. The container you select can be based on personal preference or particular needs (for example Spring.NET is actually a much larger framework that includes DI functionality; you may want to use Spring.NET for those other features or because you also use Spring in the Java world).
Lastly, Part 4 covers MEF (Managed Extensibility Framework). While MEF is not a DI container, it is often used for DI purposes. (This is true of the Prism framework -- it supports using MEF as a DI container.) Seemann shows that while MEF can be used for DI, it probably should not be. Again, this is a controversial topic: I have seen blog articles where people moved from Unity to MEF for DI and see no reason why anyone would want to start a new project with Unity. Seemann lays out some very good points regarding how DI is implemented using MEF and how it varies greatly from the other containers.
Resources
Where Dependency Injection in .NET really shines is in its use of external references. Many sections point to books, magazine articles or blog articles where you can research topics further. Seemann has gone to a lot of work to collect these resources together in one place. I will be spending a lot of time going through the links collected at the back of the book. On a good note (personally), I found that several of the books mentioned are already in my collection.
Wrap Up
Dependency Injection in .NET is an excellent resource. There are not very many DI books on the market, and it is great to see that this particular book is so well executed. I would recommend this to any developer who is interested in improving his/her use of Dependency Injection.
Happy Coding!
Monday, September 3, 2012
Monday, August 27, 2012
Steal My Windows 8 Idea: Share With Grandma
A couple weeks ago, I attended a user group where Danny Warren from InterKnowlogy presented on Windows 8 (Danny is @dannydwarren on Twitter and blogs here). He showed some really cool Windows 8 contracts including Search and Sharing. (For more info on Sharing, Danny points to a blog article about sharing here: Windows 8 and the future of XAML: Part 5: More contracts in WinRT/Windows 8.)
My brain puts things together slowly sometimes. I could tell during the presentation that Sharing was a very important feature. And over the last week or so, the idea has really settled in, and I'm really looking forward to having that feature available in my day-to-day activities.
Before Windows 8 Sharing
We love to get data from one application to another: in a photo album, we can send a photo to Facebook; in a browser, we can email a story to a friend. And historically, it has been up to each application to support the sharing. This meant that we were locked into whatever systems the application supported. I don't know about you, but after I find an application I like, I tend to hang onto it for a while. And even though it's really cool that my photo album supports sending to MySpace, it's not very relevant anymore.
This leaves me with a hard decision: do I look for another photo album and learn a new interface? Or do I settle for doing manual uploads to my favorite sharing site? Neither option is very appealing. Wouldn't it be cool if I could keep my photo album application and add whatever sharing I want?
Share Target: I Love This Idea
In Windows 8, Microsoft has separated the responsibilities of this process. The sharing application (a "Share Source") just has to expose some piece of data (whether it's text, an image, a URL, or something else). Then any application that knows how to consume that data (a "Share Target") can get access to it -- with the user's permission, of course.
I think of this as the "Send To" menu that we had in Windows XP. You could open up the "Send To" folder (under the user profile) and add shortcuts to applications. I would always add "Notepad" to my Send To options. That way, I could always right-click on a file, and, regardless of type, I could "Send To" Notepad. This was great for bypassing the default editor for a particular file. (As a side note: I've been weaned off of this functionality in Windows 7. I'm sure there's a way of customizing the Send To menu, but it's not as obvious as it was in XP).
The Share Target takes this a step further. Now, I can have any number of applications that are designated as Share Targets for photos. From inside my photo album, I just have to "Share" a photo, and then I get to select from all of the applications that can accept that photo. This means that 2 years from now when everyone is using a new photo sharing site / social network, I just need to get the latest Share Target application, and my original photo album gets to stay exactly the same.
To me, this is a brilliant idea. Several of the phone OSes have integrated Facebook, Twitter, or G+ to make it easy to post photos or send updates. But those are still bound to the OS. With Windows 8, we can share with whatever we want (assuming someone has written an application for it), and we're not locked down to whatever was popular at the time a particular piece of software was written.
Steal My Idea: Share With Grandma
So, I thought of a good idea for this feature: Share With Grandma. This would be a Share Target application where you could configure how tech-savvy Grandma is -- from Pony Express to Gadget Granny. The application would decide how to get the shared information to Grandma based on these settings.
For example, let's say that I want to share a picture with Grandma. If she has limited technical skills, then the application could send an email attachment. If she is a little bit more comfortable, then maybe it sends her a link to a Facebook update. If she's a Gadget Granny, then maybe it posts to a shared photo stream that automatically shows up on her tablet. The same type of thing could be done for text or URLs.
In all honesty, I probably won't get around to programming this. So, feel free to steal my idea (but at least give me a mention in the credits).
I see Windows 8 Sharing as a huge opportunity to come up with some really creative and useful ways of using the data that we've already got. It's time to start busting out some code.
Happy Coding!
My brain puts things together slowly sometimes. I could tell during the presentation that Sharing was a very important feature. And over the last week or so, the idea has really settled in, and I'm really looking forward to having that feature available in my day-to-day activities.
Before Windows 8 Sharing
We love to get data from one application to another: in a photo album, we can send a photo to Facebook; in a browser, we can email a story to a friend. And historically, it has been up to each application to support the sharing. This meant that we were locked into whatever systems the application supported. I don't know about you, but after I find an application I like, I tend to hang onto it for a while. And even though it's really cool that my photo album supports sending to MySpace, it's not very relevant anymore.
This leaves me with a hard decision: do I look for another photo album and learn a new interface? Or do I settle for doing manual uploads to my favorite sharing site? Neither option is very appealing. Wouldn't it be cool if I could keep my photo album application and add whatever sharing I want?
Share Target: I Love This Idea
In Windows 8, Microsoft has separated the responsibilities of this process. The sharing application (a "Share Source") just has to expose some piece of data (whether it's text, an image, a URL, or something else). Then any application that knows how to consume that data (a "Share Target") can get access to it -- with the user's permission, of course.
I think of this as the "Send To" menu that we had in Windows XP. You could open up the "Send To" folder (under the user profile) and add shortcuts to applications. I would always add "Notepad" to my Send To options. That way, I could always right-click on a file, and, regardless of type, I could "Send To" Notepad. This was great for bypassing the default editor for a particular file. (As a side note: I've been weaned off of this functionality in Windows 7. I'm sure there's a way of customizing the Send To menu, but it's not as obvious as it was in XP).
The Share Target takes this a step further. Now, I can have any number of applications that are designated as Share Targets for photos. From inside my photo album, I just have to "Share" a photo, and then I get to select from all of the applications that can accept that photo. This means that 2 years from now when everyone is using a new photo sharing site / social network, I just need to get the latest Share Target application, and my original photo album gets to stay exactly the same.
To me, this is a brilliant idea. Several of the phone OSes have integrated Facebook, Twitter, or G+ to make it easy to post photos or send updates. But those are still bound to the OS. With Windows 8, we can share with whatever we want (assuming someone has written an application for it), and we're not locked down to whatever was popular at the time a particular piece of software was written.
Steal My Idea: Share With Grandma
So, I thought of a good idea for this feature: Share With Grandma. This would be a Share Target application where you could configure how tech-savvy Grandma is -- from Pony Express to Gadget Granny. The application would decide how to get the shared information to Grandma based on these settings.
For example, let's say that I want to share a picture with Grandma. If she has limited technical skills, then the application could send an email attachment. If she is a little bit more comfortable, then maybe it sends her a link to a Facebook update. If she's a Gadget Granny, then maybe it posts to a shared photo stream that automatically shows up on her tablet. The same type of thing could be done for text or URLs.
In all honesty, I probably won't get around to programming this. So, feel free to steal my idea (but at least give me a mention in the credits).
I see Windows 8 Sharing as a huge opportunity to come up with some really creative and useful ways of using the data that we've already got. It's time to start busting out some code.
Happy Coding!
Sunday, August 19, 2012
Dependency Injection: How Do You Find the Balance?
As a developer, I am constantly trying to find the right balance -- to figure out the right level of abstraction for the current project. If we add too much (unneeded) abstraction, we end up with code that may be more difficult to debug and maintain. If we add too little (needed) abstraction, we end up with code that is difficult to extend and maintain. Somewhere in-between, we have a good balance that leads to the optimum level of maintainability for the current environment.
Technique 1: Add It When You Need It
I'm a big fan of not adding abstraction until you need it. This is a technique that Robert C. Martin recommends in Agile Principles, Patterns, and Practices in C# (Amazon Link). This is my initial reaction to abstractions in code -- primarily because I've been burned by some really badly implemented abstractions in the past. After dealing with abstractions that didn't add benefit to the application (and only complicated maintenance), my kneejerk reaction is to not add abstractions until really necessary.
This is not to say that abstractions are bad. We just need to make sure that they are relevant to the code that we are building. The bad implementations that I've run across have generally been the result of what I call "white paper architecture". This happens when the application designer reads a white paper on how to architect an application and decides to implement it without considering the specific implications in the environment. I'll give 2 examples.
Example 1: I ended up as primary support on an application that made use of base classes for the forms. In itself, this isn't a bad thing. The problem was in the implementation. If you did not use the base class, then the form would not work at all. This led to much gnashing of teeth. In a more useful scenario, a base class would add specific functionality. But if the base class was not used, the form would still work (just without the extra features).
Example 2: I helped someone out on another project (fortunately, I didn't end up supporting this application myself). This application was abstracted out too far. In order to add a new field (meaning, from the data store to the screen), it was necessary to modify 17 files (from data storage, through ORM, objects on the server side, DTOs on the server side, through the service, DTOs on the client side, objects on the client side, to the presentation layer). And unfortunately, if you missed a file it did not result in a compile-time error; it would show up as a run-time error.
After coming across several application like these, I've adopted the YAGNI principle (You Aren't Gonna Need It). If you do need it later, then you can add it.
Technique 2: Know Your Environment
Unfortunately, Technique 1 isn't always feasible. It is often time consuming to go back into an application and add the abstractions as you need them. When we're asked as developers to keep a specific delivery velocity, we're not often given time to go back and refactor things later. So, a more practical option comes with experience: know the environment that you're working in.
As an example, for many years I worked in an environment that used Microsoft SQL Server. That was our database platform, and every application that we built used SQL Server. Because of this, I didn't spend time doing a full abstraction of the data layer. This doesn't mean that I had database calls sprinkled through the code. What it means is that I had a logical separation of the database calls (meaning that DB calls were only made in specific parts of the library) but didn't have a physical separation (for example, with a repository interface that stood in front of the database).
Was this bad design? I know several folks who would immediately say "Yes, that's terrible design." But I would argue that it was good design for the application environment.
Out of the 20 or so applications that I built while at that company, a grand total of one application needed to support a different database (an application that pulled data from a vendor product that used an Oracle backend). For that one application, I added a database abstraction layer (this was actually a conversion -- the vendor product was originally using SQL Server and was changed to Oracle during an upgrade). So what makes more sense? To add an unused abstraction to 20 applications? Or to add the necessary abstraction to the one application that actually needed it?
Now, if I was building an application for a different environment that needed to support different data stores (such as software that would be delivered to different customer sites), I would design things much differently. You can see a simple example of how I would design this here: IEnumerable, ISaveable, IDontGetIt: Understanding .NET Interfaces.
Unfortunately, this type of decision can only be made if you know your environment well. It usually takes years of experience in that environment to know which things are likely to change and which things are likely to stay the same. When you walk into a new environment, it can be very difficult to figure out how to make these distinctions.
Dependency Injection: Getting Things "Just Right"
My current project is a WPF project using Prism. Prism is a collection of libraries and guidance around building XAML applications, and a big part of that guidance is around Dependency Injection (DI). I've been doing quite a bit of programming (and thinking) around dependency injection over the last couple months, and I'm still trying to find the balance -- the Goldilocks "Just Right" between "Too Loosely Coupled" and "Too Tightly Coupled".
Did I just say "Too Loosely Coupled"? Is that even possible? We're taught that loose coupling is a good thing -- something we should always be striving for. And I would venture to guess that there are many developers out there who would say that there's no such thing as "too loosely coupled."
But the reason that loose coupling is promoted so highly is that our problem is usually the opposite -- the default state of application developers is to have tight coupling. Loose coupling is encouraged because it's not our instinctual reaction.
I'm currently reading Mark Seemann's Dependency Injection in .NET (Amazon Link). This is an excellent book (disclaimer: I've only read half of it so far, but I don't expect that my evaluation will change). Seemann describes many of the patterns and anti-patterns in Dependency Injection along with the benefits and costs (which helps us decide when/where to use specific patterns).
An important note: Seemann specifically says that the sample application that he shows will be more complicated than most DI samples he's seen. He does this because DI doesn't make sense in a "simple" application; the value really shines in complex applications that have many functions that should be broken out. With the functions broken out into separate classes, it makes sense to make sure that the classes are loosely coupled so that we can add/remove/change/decorate implementations without needing to modify all of our code. This means that not all applications benefit from DI; the benefits come once we hit a certain level of complexity.
So, now we have to decide how much dependency injection is "Just Right". As an example, Seemann describes the Service Locator as an anti-pattern. But Prism has a built-in Service Locator. So, should we use the Prism Service Locator or not? And that's where we come back to the balance of "it depends."
In the application I'm working on, we are using the Service Locator pattern, and it seems to be working well for those parts of the library. I have run into a few interesting issues (specifically when writing unit tests for these classes), and it turns out that Seemann points out exactly the issues that I've been thinking about.
I don't really have time to go into the details here. As an example, when using the Service Locator, it is difficult to see the specific dependencies for a class. As we have been modifying modules during our build, sometimes the unit tests are breaking because a new dependency was added (which is resolved by the Service Locator), but it doesn't stop the code from compiling. We then need to modify our unit tests by adding/mocking the new dependency.
[Editor's Note: I've published an article talking more about the pros and cons of the Service Locator pattern: Dependency Injection: The Service Locator Pattern.]
As with everything, there are pros and cons. For the time being, I'm content with using the Service Locator for our application. There are some "gotchas" that I need to look out for (but that's true with whatever patterns I'm using). Seemann also notes that he was once a proponent of Service Locator and moved away from it after he discovered better approaches that would eliminate the disadvantages that he was running across. It may be that I come to that same conclusion after working with Service Locator for a while. Time will tell.
How Do You Do Dependency Injection?
Now it's time to start a conversation. How do you use Dependency Injection? What has worked well for you in different types of applications and environments? Do you have any favorite DI references / articles that have pointed you in a direction that works well for you?
As an aside, Mark Seemann's book has tons of reference articles -- most pages have some sort of footnote referring to a book or article on the topic. It is very evident that Seemann has researched the topic very thoroughly. I'm going to try to read through as many of these references as I can find time for.
Drop your experiences in the comments, and we can all learn from each other.
Happy Coding!
Technique 1: Add It When You Need It
I'm a big fan of not adding abstraction until you need it. This is a technique that Robert C. Martin recommends in Agile Principles, Patterns, and Practices in C# (Amazon Link). This is my initial reaction to abstractions in code -- primarily because I've been burned by some really badly implemented abstractions in the past. After dealing with abstractions that didn't add benefit to the application (and only complicated maintenance), my kneejerk reaction is to not add abstractions until really necessary.
This is not to say that abstractions are bad. We just need to make sure that they are relevant to the code that we are building. The bad implementations that I've run across have generally been the result of what I call "white paper architecture". This happens when the application designer reads a white paper on how to architect an application and decides to implement it without considering the specific implications in the environment. I'll give 2 examples.
Example 1: I ended up as primary support on an application that made use of base classes for the forms. In itself, this isn't a bad thing. The problem was in the implementation. If you did not use the base class, then the form would not work at all. This led to much gnashing of teeth. In a more useful scenario, a base class would add specific functionality. But if the base class was not used, the form would still work (just without the extra features).
Example 2: I helped someone out on another project (fortunately, I didn't end up supporting this application myself). This application was abstracted out too far. In order to add a new field (meaning, from the data store to the screen), it was necessary to modify 17 files (from data storage, through ORM, objects on the server side, DTOs on the server side, through the service, DTOs on the client side, objects on the client side, to the presentation layer). And unfortunately, if you missed a file it did not result in a compile-time error; it would show up as a run-time error.
After coming across several application like these, I've adopted the YAGNI principle (You Aren't Gonna Need It). If you do need it later, then you can add it.
Technique 2: Know Your Environment
Unfortunately, Technique 1 isn't always feasible. It is often time consuming to go back into an application and add the abstractions as you need them. When we're asked as developers to keep a specific delivery velocity, we're not often given time to go back and refactor things later. So, a more practical option comes with experience: know the environment that you're working in.
As an example, for many years I worked in an environment that used Microsoft SQL Server. That was our database platform, and every application that we built used SQL Server. Because of this, I didn't spend time doing a full abstraction of the data layer. This doesn't mean that I had database calls sprinkled through the code. What it means is that I had a logical separation of the database calls (meaning that DB calls were only made in specific parts of the library) but didn't have a physical separation (for example, with a repository interface that stood in front of the database).
Was this bad design? I know several folks who would immediately say "Yes, that's terrible design." But I would argue that it was good design for the application environment.
Out of the 20 or so applications that I built while at that company, a grand total of one application needed to support a different database (an application that pulled data from a vendor product that used an Oracle backend). For that one application, I added a database abstraction layer (this was actually a conversion -- the vendor product was originally using SQL Server and was changed to Oracle during an upgrade). So what makes more sense? To add an unused abstraction to 20 applications? Or to add the necessary abstraction to the one application that actually needed it?
Now, if I was building an application for a different environment that needed to support different data stores (such as software that would be delivered to different customer sites), I would design things much differently. You can see a simple example of how I would design this here: IEnumerable, ISaveable, IDontGetIt: Understanding .NET Interfaces.
Unfortunately, this type of decision can only be made if you know your environment well. It usually takes years of experience in that environment to know which things are likely to change and which things are likely to stay the same. When you walk into a new environment, it can be very difficult to figure out how to make these distinctions.
Dependency Injection: Getting Things "Just Right"
My current project is a WPF project using Prism. Prism is a collection of libraries and guidance around building XAML applications, and a big part of that guidance is around Dependency Injection (DI). I've been doing quite a bit of programming (and thinking) around dependency injection over the last couple months, and I'm still trying to find the balance -- the Goldilocks "Just Right" between "Too Loosely Coupled" and "Too Tightly Coupled".
Did I just say "Too Loosely Coupled"? Is that even possible? We're taught that loose coupling is a good thing -- something we should always be striving for. And I would venture to guess that there are many developers out there who would say that there's no such thing as "too loosely coupled."
But the reason that loose coupling is promoted so highly is that our problem is usually the opposite -- the default state of application developers is to have tight coupling. Loose coupling is encouraged because it's not our instinctual reaction.
I'm currently reading Mark Seemann's Dependency Injection in .NET (Amazon Link). This is an excellent book (disclaimer: I've only read half of it so far, but I don't expect that my evaluation will change). Seemann describes many of the patterns and anti-patterns in Dependency Injection along with the benefits and costs (which helps us decide when/where to use specific patterns).
An important note: Seemann specifically says that the sample application that he shows will be more complicated than most DI samples he's seen. He does this because DI doesn't make sense in a "simple" application; the value really shines in complex applications that have many functions that should be broken out. With the functions broken out into separate classes, it makes sense to make sure that the classes are loosely coupled so that we can add/remove/change/decorate implementations without needing to modify all of our code. This means that not all applications benefit from DI; the benefits come once we hit a certain level of complexity.
So, now we have to decide how much dependency injection is "Just Right". As an example, Seemann describes the Service Locator as an anti-pattern. But Prism has a built-in Service Locator. So, should we use the Prism Service Locator or not? And that's where we come back to the balance of "it depends."
In the application I'm working on, we are using the Service Locator pattern, and it seems to be working well for those parts of the library. I have run into a few interesting issues (specifically when writing unit tests for these classes), and it turns out that Seemann points out exactly the issues that I've been thinking about.
I don't really have time to go into the details here. As an example, when using the Service Locator, it is difficult to see the specific dependencies for a class. As we have been modifying modules during our build, sometimes the unit tests are breaking because a new dependency was added (which is resolved by the Service Locator), but it doesn't stop the code from compiling. We then need to modify our unit tests by adding/mocking the new dependency.
[Editor's Note: I've published an article talking more about the pros and cons of the Service Locator pattern: Dependency Injection: The Service Locator Pattern.]
As with everything, there are pros and cons. For the time being, I'm content with using the Service Locator for our application. There are some "gotchas" that I need to look out for (but that's true with whatever patterns I'm using). Seemann also notes that he was once a proponent of Service Locator and moved away from it after he discovered better approaches that would eliminate the disadvantages that he was running across. It may be that I come to that same conclusion after working with Service Locator for a while. Time will tell.
How Do You Do Dependency Injection?
Now it's time to start a conversation. How do you use Dependency Injection? What has worked well for you in different types of applications and environments? Do you have any favorite DI references / articles that have pointed you in a direction that works well for you?
As an aside, Mark Seemann's book has tons of reference articles -- most pages have some sort of footnote referring to a book or article on the topic. It is very evident that Seemann has researched the topic very thoroughly. I'm going to try to read through as many of these references as I can find time for.
Drop your experiences in the comments, and we can all learn from each other.
Happy Coding!
Wednesday, August 1, 2012
Los Angeles .NET Developers Group
If you're in the Los Angeles area, I'll be speaking at the Los Angeles .NET Devleopers Group on Monday, August 6th. More info is available here: http://www.ladotnet.org/events/74859482/.
The topic is "Learn the Lingo: Design Patterns". We'll take a look at what design patterns are, why they are important to us, and how we already use them in our everyday code without even realizing it. In addition to the benefits, we'll take a look at the costs to help us determine when we should be using various patterns. Once we are familiar with Design Patterns, we can start to use them deliberately and hopefully get to the point where we are using them automatically (where appropriate, of course). Hope to see you there.
Happy Coding!
The topic is "Learn the Lingo: Design Patterns". We'll take a look at what design patterns are, why they are important to us, and how we already use them in our everyday code without even realizing it. In addition to the benefits, we'll take a look at the costs to help us determine when we should be using various patterns. Once we are familiar with Design Patterns, we can start to use them deliberately and hopefully get to the point where we are using them automatically (where appropriate, of course). Hope to see you there.
Happy Coding!
Sunday, July 22, 2012
First Impressions: Working with Prism in WPF
Three weeks ago, I started working with Prism on a WPF application. Here are a few of my first thoughts:
The Good
Prism is an extremely powerful framework. It provides classes to help you manage all parts of your WPF application (or Silverlight or Windows Phone 7 -- I'm sure that Metro will be coming soon). So far, I've worked with the following features:
Prism is not for beginning programmers. If you do not have a solid grasp on intermediate .NET topics, then much of the Prism framework is going to look like magic incantations. This can only lead to misuse of the framework classes, and most likely this misuse will lead to not getting the benefits (such as good separation of concerns, re-use, and testability).
Before using Prism, you should have a good foundation with the following topics:
Good Collection of Features
Prism is an extremely powerful framework and has gone through several iterations and refinements. The current version (4.1) even includes support for Silverlight 5. The Patterns & Practices team has obviously put a lot of work into the framework, and my experience so far has been bug-free (from the framework perspective at least -- I'm working on making my own code bug-free). Let's take a closer look at the features that I've experienced so far:
Modules / Module Catalog
Modules allow you to separate your application functionality into discrete units that can be "plugged in" to the application. The modules can (should?) be designed to be application agnostic and self-contained. This means that they can be re-used in other applications. For example, if you have a need for Customer Maintenance, this could be its own module. If you have proper separation of concerns, then this module could be used in multiple applications.
For the application I am working on, we have modularized most of the application "screens". These are being tied together through a work flow. As a (fictional) example, to take a customer order over the phone, you could start with the Customer module (either entering new customer data or selecting an existing customer), moving on to a Shopping Cart module (where products and quantities are selected), then to a Shipping module, then to a Payment module. If these modules are designed to work in isolation, then they can be reused in the same application in different work flows.
The Module Catalog keeps track of all of the modules in the application. This can be set explicitly by specifying types and assemblies, or it can be done much more dynamically. In our project, the bootstrapper process (that loads the module catalog) searches all of the assemblies in a particular folder and catalogs / initializes any modules that it finds in there.
UI Composition / Regions / Region Manager
The Region Manager is another very powerful tool. It lets you associate different Views with different Regions of the UI. This allows you to bring several Views together into a single "screen" for the user. Different Views can interact with each other (through the Event Aggregator) and still remain programmatically isolated. The idea is very similar to how a Master Page in an ASP.NET application allows you to have multiple web forms shown in different areas of a single "page".
Navigation
The Navigation infrastructure adds quite a bit of functionality to moving between views. For example, what do you do if you have unsaved data in a View and the user wants to navigate to another View? WPF does not have anything built in for this scenario, but Prism does.
Prism offers the IConfirmNavigationRequest interface that can be added to your Views or ViewModels (as a side note, many of the Prism features can be added to either the View or the ViewModel and they behave the same -- this gives you great flexibility depending on whether you are coding View-first or ViewModel-first in your application). This interface has a ConfirmNavigationRequest method.
When using the Navigation infrastructure, you call "RequestNavigate" (notice the word "request"). The method parameters include a callback that runs when the navigation completes successfully. But, when RequestNavigate is called, ConfirmNavigationRequest is fired on the View/ViewModel that you are navigating from. This gives that View/ViewModel the chance to confirm or cancel the navigation. For example, if the current View/ViewModel has unsaved data, it can prompt the user to Save, Discard Changes, or Cancel. Cancel would then cancel the navigation, and the current View would remain in place. Otherwise, the "from" View/ViewModel can simply call the callback, and navigation completes normally.
There are also other methods in the interface (part of INavigationAware and IConfirmNavigationRequest) that allow you to run code "OnNavigatedFrom" (to clean up any resources in the current View/ViewModel) or "OnNavigatedTo" (to initialize the new View/ViewModel).
This functionality is very powerful, and if you did not use what comes with Prism, you would probably end up building a lot of this yourself. I speak from experience: I implemented a much simpler ICloseQueryable interface on a simple form manager used in a non-Prism application (for more information, see XAML App Review - Part 2: Form Manager).
Dependency Injection
You won't be able to use Prism effectively without dependency injection. In order to have good separation of concerns for the modularity, MVVM presentation pattern, and other cross-cutting concerns, Prism needs a dependency injection container. Prism ships with implementations for using Unity (from Patterns & Practices) and MEF (built into .NET 4.0), but you can build your own implementations for using a different DI container (or just cruise the web; I'm sure other people have created implementations for the most popular DI containers).
I've been working with Unity on our project. With Unity, you can use both Constructor Injection (where the dependencies are specified as parameters in the constructor) and Property Injection (where public properties are flagged as "Dependency" and automatically resolved).
Dependency Injection lets your Views/ViewModels get access to the cross-cutting classes in your application. For example, you could have an Authorization or Logging service/class that gets injected into each View/ViewModel. This way, if the Module is used in a different application, the ViewModels will automatically get the cross-cutting services that are registered for that application.
In a Prism application you will (hopefully) make heavy use of interfaces. For example, the ViewModel could have a dependency on a Model interface, and this dependency could be injected with constructor injection. This gives you the flexibility of swapping out the Model when you want to unit test the ViewModel.
In this scenario, the application would use the DI container to associate the IModel with the concrete Model (with "RegisterType" if you're using Unity). If this registration is done at the Module level, then the dependency can be correctly injected everywhere it is used. In the unit tests, you could create a mock of IModel that is registered with the test container. This makes it very easy to unit test your ViewModel without having to worry about a specific implementation of that Model (such as one that needs a network connection to a service).
I'll be talking more about Unit Testing with Prism and Unity in a future article. That's an interesting topic in and of itself.
Event Aggregation
With all the loose-coupling that is going on in Prism, it could be very difficult to communicate between modules. This is where the EventAggregator comes in. The EventAggregator allows one module to Publish an event (with a particular payload), and any other module can Subscribe to that event. The two modules do not need to know directly about one another. One is simply publishing an event, and the other is simply listening for an event. Neither cares about where the event is coming from nor where it's going.
The EventAggregator takes things another step beyond the normal eventing model in .NET. The Subscribe method also gives you the opportunity put in a condition based on the payload. For example, you could say that you want to Subscribe to a StockUpdateEvent, but only if the payload has a StockTickerID of "GM". The event is published normally, but your event handler only fires if the payload has that particular value in it.
Other "Helper" Objects
Prism also provides helper objects that are designed to make life easier (often by reducing boiler-plate code). Two of my favorites are NotificationObject and DelegateCommand.
The NotificationObject (Prism) is a base class that implements INotifyPropertyChanged (.NET). Pretty much all of your ViewModels and/or Models will need to implement INotifyPropertyChanged in order for the data binding to behave as expected. NotifcationObject is a concrete class that implements INotifyPropertyChanged, so you don't need to include the boiler-plate code for that interface in every single object.
DelegateCommand (Prism) simplifies commanding and the implementation of ICommand (.NET). Commanding is a much larger topic, but the usual process is to create a class that implements the ICommand interface (this can be a class embedded in the class that uses it or a completely separate class). ICommand has 2 primary methods: Execute (which is the command code) and CanExecute (which determines whether the command can be run -- this can enable/disable a button tied to the command based on a particular state).
DelegateCommand replaces this separate class with a constructor that takes 1 or 2 delegates as parameters. The first delegate is the code for the command to run ("Execute") and the second delegate is for the "CanExecute" (if you don't provide this, it will always be "true"). Again, this class helps to cut down the boiler-plate code of creating an ICommand object and eliminates the need for a separate type. Since it is part of your class, the delegates also have access to the internal members/state of the containing class.
Putting It All Together
These features all come together with the goal of making the application easier to maintain, more testable, and reusable.
One main thing to note: you don't have to use all of these features. If you decide that you do not want to use the Region Manager or Navigation, that's fine. If you don't want to use the Event Aggregator, that's fine, too. You can use just the features that you want. Also Prism does not assume that you are using the MVVM pattern; you don't need to use MVVM, but Prism provides helpers to make the pattern easier to implement. And even though the features are mostly optional, I think that you'll find yourself using most of these features for any non-trivial application.
I've read about Prism (and previously about the Composite Application Guidance for WPF) and have found it interesting. At the time, I was concerned about a lot of the apparent magic that is going on (and I am still concerned about this). It is imperative that you have a good handle on the foundational principles used in Prism before you get started. If you have a good foundation, then you can build very solid (yet de-coupled, maintainable, testable, and modular) applications.
Three weeks is not that long to have worked with a framework such as Prism. I'm amazed at how much more I know about Prism than I did prior to this project; much of this is due to a couple of folks on the team who have used Prism successfully in prior projects. There's still some learning to do on the finer points, and we are still tweaking our implementations a bit, but the larger functionality has fallen into place.
[Update: Here are some thoughts after working with Prism for a while: Five Months In: Working with Prism in WPF]
Happy Coding!
The Good
Prism is an extremely powerful framework. It provides classes to help you manage all parts of your WPF application (or Silverlight or Windows Phone 7 -- I'm sure that Metro will be coming soon). So far, I've worked with the following features:
- Modules / Module Catalog
- UI Composition / Regions / Region Manager
- Navigation
- Dependency Injection
- Event Aggregation
- Other "Helper" Objects
Prism is not for beginning programmers. If you do not have a solid grasp on intermediate .NET topics, then much of the Prism framework is going to look like magic incantations. This can only lead to misuse of the framework classes, and most likely this misuse will lead to not getting the benefits (such as good separation of concerns, re-use, and testability).
Before using Prism, you should have a good foundation with the following topics:
- Dependency Injection / Inversion of Control
- Interfaces
- Delegates / Func<T> / Action<T>
- Lambda Expressions
- Events and Event Handlers
- Model-View-ViewModel (MVVM)
- Various other Design Patterns
As a side note, the documentation ("A Developer's Guide to Microsoft Prism 4" -- available as a PDF or as a tree-book) has an appendix dedicated to the primary design patterns used by Prism. This includes Adapter, Application Controller Pattern, Command Pattern, Composite and Composite View, Dependency Injection Pattern, Event Aggregator Pattern, Facade Pattern, Inversion of Control Pattern, Observer Pattern, Presentation Model Pattern, Registry Pattern, Repository Pattern, Separated Interface and Plug-In, and Service Locator Pattern.
Prism is an extremely powerful framework and has gone through several iterations and refinements. The current version (4.1) even includes support for Silverlight 5. The Patterns & Practices team has obviously put a lot of work into the framework, and my experience so far has been bug-free (from the framework perspective at least -- I'm working on making my own code bug-free). Let's take a closer look at the features that I've experienced so far:
Modules / Module Catalog
Modules allow you to separate your application functionality into discrete units that can be "plugged in" to the application. The modules can (should?) be designed to be application agnostic and self-contained. This means that they can be re-used in other applications. For example, if you have a need for Customer Maintenance, this could be its own module. If you have proper separation of concerns, then this module could be used in multiple applications.
For the application I am working on, we have modularized most of the application "screens". These are being tied together through a work flow. As a (fictional) example, to take a customer order over the phone, you could start with the Customer module (either entering new customer data or selecting an existing customer), moving on to a Shopping Cart module (where products and quantities are selected), then to a Shipping module, then to a Payment module. If these modules are designed to work in isolation, then they can be reused in the same application in different work flows.
The Module Catalog keeps track of all of the modules in the application. This can be set explicitly by specifying types and assemblies, or it can be done much more dynamically. In our project, the bootstrapper process (that loads the module catalog) searches all of the assemblies in a particular folder and catalogs / initializes any modules that it finds in there.
UI Composition / Regions / Region Manager
The Region Manager is another very powerful tool. It lets you associate different Views with different Regions of the UI. This allows you to bring several Views together into a single "screen" for the user. Different Views can interact with each other (through the Event Aggregator) and still remain programmatically isolated. The idea is very similar to how a Master Page in an ASP.NET application allows you to have multiple web forms shown in different areas of a single "page".
Navigation
The Navigation infrastructure adds quite a bit of functionality to moving between views. For example, what do you do if you have unsaved data in a View and the user wants to navigate to another View? WPF does not have anything built in for this scenario, but Prism does.
Prism offers the IConfirmNavigationRequest interface that can be added to your Views or ViewModels (as a side note, many of the Prism features can be added to either the View or the ViewModel and they behave the same -- this gives you great flexibility depending on whether you are coding View-first or ViewModel-first in your application). This interface has a ConfirmNavigationRequest method.
When using the Navigation infrastructure, you call "RequestNavigate" (notice the word "request"). The method parameters include a callback that runs when the navigation completes successfully. But, when RequestNavigate is called, ConfirmNavigationRequest is fired on the View/ViewModel that you are navigating from. This gives that View/ViewModel the chance to confirm or cancel the navigation. For example, if the current View/ViewModel has unsaved data, it can prompt the user to Save, Discard Changes, or Cancel. Cancel would then cancel the navigation, and the current View would remain in place. Otherwise, the "from" View/ViewModel can simply call the callback, and navigation completes normally.
There are also other methods in the interface (part of INavigationAware and IConfirmNavigationRequest) that allow you to run code "OnNavigatedFrom" (to clean up any resources in the current View/ViewModel) or "OnNavigatedTo" (to initialize the new View/ViewModel).
This functionality is very powerful, and if you did not use what comes with Prism, you would probably end up building a lot of this yourself. I speak from experience: I implemented a much simpler ICloseQueryable interface on a simple form manager used in a non-Prism application (for more information, see XAML App Review - Part 2: Form Manager).
Dependency Injection
You won't be able to use Prism effectively without dependency injection. In order to have good separation of concerns for the modularity, MVVM presentation pattern, and other cross-cutting concerns, Prism needs a dependency injection container. Prism ships with implementations for using Unity (from Patterns & Practices) and MEF (built into .NET 4.0), but you can build your own implementations for using a different DI container (or just cruise the web; I'm sure other people have created implementations for the most popular DI containers).
I've been working with Unity on our project. With Unity, you can use both Constructor Injection (where the dependencies are specified as parameters in the constructor) and Property Injection (where public properties are flagged as "Dependency" and automatically resolved).
Dependency Injection lets your Views/ViewModels get access to the cross-cutting classes in your application. For example, you could have an Authorization or Logging service/class that gets injected into each View/ViewModel. This way, if the Module is used in a different application, the ViewModels will automatically get the cross-cutting services that are registered for that application.
In a Prism application you will (hopefully) make heavy use of interfaces. For example, the ViewModel could have a dependency on a Model interface, and this dependency could be injected with constructor injection. This gives you the flexibility of swapping out the Model when you want to unit test the ViewModel.
In this scenario, the application would use the DI container to associate the IModel with the concrete Model (with "RegisterType" if you're using Unity). If this registration is done at the Module level, then the dependency can be correctly injected everywhere it is used. In the unit tests, you could create a mock of IModel that is registered with the test container. This makes it very easy to unit test your ViewModel without having to worry about a specific implementation of that Model (such as one that needs a network connection to a service).
I'll be talking more about Unit Testing with Prism and Unity in a future article. That's an interesting topic in and of itself.
Event Aggregation
With all the loose-coupling that is going on in Prism, it could be very difficult to communicate between modules. This is where the EventAggregator comes in. The EventAggregator allows one module to Publish an event (with a particular payload), and any other module can Subscribe to that event. The two modules do not need to know directly about one another. One is simply publishing an event, and the other is simply listening for an event. Neither cares about where the event is coming from nor where it's going.
The EventAggregator takes things another step beyond the normal eventing model in .NET. The Subscribe method also gives you the opportunity put in a condition based on the payload. For example, you could say that you want to Subscribe to a StockUpdateEvent, but only if the payload has a StockTickerID of "GM". The event is published normally, but your event handler only fires if the payload has that particular value in it.
Other "Helper" Objects
Prism also provides helper objects that are designed to make life easier (often by reducing boiler-plate code). Two of my favorites are NotificationObject and DelegateCommand.
The NotificationObject (Prism) is a base class that implements INotifyPropertyChanged (.NET). Pretty much all of your ViewModels and/or Models will need to implement INotifyPropertyChanged in order for the data binding to behave as expected. NotifcationObject is a concrete class that implements INotifyPropertyChanged, so you don't need to include the boiler-plate code for that interface in every single object.
DelegateCommand (Prism) simplifies commanding and the implementation of ICommand (.NET). Commanding is a much larger topic, but the usual process is to create a class that implements the ICommand interface (this can be a class embedded in the class that uses it or a completely separate class). ICommand has 2 primary methods: Execute (which is the command code) and CanExecute (which determines whether the command can be run -- this can enable/disable a button tied to the command based on a particular state).
DelegateCommand replaces this separate class with a constructor that takes 1 or 2 delegates as parameters. The first delegate is the code for the command to run ("Execute") and the second delegate is for the "CanExecute" (if you don't provide this, it will always be "true"). Again, this class helps to cut down the boiler-plate code of creating an ICommand object and eliminates the need for a separate type. Since it is part of your class, the delegates also have access to the internal members/state of the containing class.
Putting It All Together
These features all come together with the goal of making the application easier to maintain, more testable, and reusable.
One main thing to note: you don't have to use all of these features. If you decide that you do not want to use the Region Manager or Navigation, that's fine. If you don't want to use the Event Aggregator, that's fine, too. You can use just the features that you want. Also Prism does not assume that you are using the MVVM pattern; you don't need to use MVVM, but Prism provides helpers to make the pattern easier to implement. And even though the features are mostly optional, I think that you'll find yourself using most of these features for any non-trivial application.
I've read about Prism (and previously about the Composite Application Guidance for WPF) and have found it interesting. At the time, I was concerned about a lot of the apparent magic that is going on (and I am still concerned about this). It is imperative that you have a good handle on the foundational principles used in Prism before you get started. If you have a good foundation, then you can build very solid (yet de-coupled, maintainable, testable, and modular) applications.
Three weeks is not that long to have worked with a framework such as Prism. I'm amazed at how much more I know about Prism than I did prior to this project; much of this is due to a couple of folks on the team who have used Prism successfully in prior projects. There's still some learning to do on the finer points, and we are still tweaking our implementations a bit, but the larger functionality has fallen into place.
[Update: Here are some thoughts after working with Prism for a while: Five Months In: Working with Prism in WPF]
Happy Coding!
Labels:
Dependency Injection,
Design Patterns,
Interfaces,
MVVM,
Prism,
WPF
Sunday, July 8, 2012
Metrocizing XAML: Part 2: Control Templates
In Part 1, we saw how we could update the look and feel of our XAML applications by changing a bit of layout and our Data Template. This time, we will take things a step further. First, we'll do a quick overview of some of the minor changes (colors and general layout), and then we'll dive into creating a custom control template for our buttons to make them more Metro-ish (Metroid?). As a reminder, here are the UIs of our "old" and "new" applications:
The source code for both of these projects is available here: Jeremy Bytes - Downloads. These applications are in the "Old.UI" and "New.UI" projects respectively.
Application-Level Updates
Several of the updates to the application have to do with the general layout and colors. You can check the XAML for more details on this. The key features are the re-arrangement of the grid (swapping the ListBox and Button panels), removal of the background gradient, and the insertion of a solid background.
As mentioned in Part 1, the resources for the application (brushes, data templates, and value converters) were moved from MainWindow.xaml to the App.xaml Resources section. Let's start by looking at the top of our App.xaml (from New.UI):
The first thing to note is that we have moved our Value Converters from MainWindow.xaml to here. This was necessary because several of the value converters are used in the ListBox Data Template that is also in this file. To bring in the Value Converters, we needed to add the namespace for the local project (so we can access the classes in Converters.cs). For more information on Value Converters and how they get added, please see Introduction to Data Templates and Value Converters in Silverlight (also works in WPF).
The next section contains brushes for our application. There were no resources for these items in the old application. I added them here so that it would be easy to update the application colors in the future (since Metro-ish applications will go out of style sometime in the future). Notice that I named the resources after what they are used for and not what colors they are. If I had named the resource something like "LightGrayBrush", then it would be difficult if we wanted to change it to another color. Since the name describes what the brush is used for (rather than what it looks like), we can change this to blue in the future without worrying about mis-matched names.
These application brushes are tied to the XAML in MainWindow.xaml (such as setting the application background). And, as we'll see, these are also used in our control templates for the buttons.
Let's see what else is in the App.xaml (sections are collapsed to get the overview):
First, we have a Control Template and Style for our "GoButton". We'll be spending quite a bit of time in this detail below. Next, we have a Control Template and Style for our "ClearButton". The buttons differ in the icons (an arrow vs. an X), but they otherwise behave the same.
Next we have 3 different TextBlock styles for "ApplicationText", "ListItemTextStyle", and "ListTextStyle". We saw the ListItemTextStyle and ListTextStyle in use in our Data Template in Part 1. Finally, we have our Data Template for the ListBox. We looked at this in detail in Part 1.
Comparing Buttons
So, what are the differences between our old and new buttons. Let's compare them side-by-side:
One big difference is operation. The old button is only clickable on the part that looks like a button (the "Fetch" part). The new button is clickable anywhere in the rectangle. This makes it much friendlier to touch-enabled applications since it is a much bigger target.
Let's compare the XAML, starting with the old button in MainWindow.xaml (in Old.UI):
Notice that we have a border that encloses a StackPanel. And that StackPanel contains a TextBlock and an actual Button.
Compare this to our new XAML (in MainWindow.xaml in New.UI):
The difference is here we just have a Button with the content of "Concrete Class". So, where's the rest of it (the border and the icon)? That's all part of the custom Control Template. And it's getting applied to this button through the Style property.
The Button Style
As we saw earlier, the GoButtonStyle is in the App.xaml. Now, let's take a look at the details:
The Style allows us to apply settings to properties centrally. We have 2 buttons in our application that use this Style (the "Concrete Class" button and the "Interface" button). But we only have to set these properties once. And if we decide that we want to change something (like the FontSize), we just update it here, and it automatically propagates to all buttons that are using this Style.
Notice that the "Foreground" and "Background" properties are set to our "Application" brush resources that we set above. This is important. We want our buttons to have the same background as the application. If the application background changes, we want our button background to change along with it. (Note: this might not always be the case, but that's the behavior that we want here.)
Finally, we have the "Template" property set to the Control Template that's also in our App.xaml.
The Button Control Template
Before we look at the specifics of the GoButton Template, I want to say a few words about Control Templates.
As mentioned in Part 1, XAML controls are "lookless". This means that the behavior is completely separated from the visual display. We are provided with default templates (so that we don't have to create our own), but the templates are fully customizable and/or replaceable.
Control Templates are generally very complex. They are designed to handle a variety of states. For example, a button has a number of states, including "Normal", "MouseOver", "Pressed", "Disabled" as well as "Focused" and "Unfocused" (in addition to others). If you want to have all of these features available, then it's often easiest to use Expression Blend to export the current control template for you to modify.
In our case, we are only handling a subset of these states (Normal, MouseOver, and Pressed), and we don't worry about the other states (since they aren't really applicable to our application). One thing to note: just because we do not implement a visual change for a State does not mean that that State does not exist. For example, our control template does not implement "Disabled", but our button can still be disabled -- it just won't look any different from an enabled button. Same with "Focused" -- the button itself still supports the idea of "focus", but it will not look any different if it is focused.
If we were creating a set of custom templates to be used more extensively, then we would definitely want to implement all of these states. As it is, we'll just focus on the ones we care about for this application.
Control Template Overview
Our Control Template is more complex than other bits of XAML that we've seen so far, so we'll break this down into several different pieces. First, let's look at an overview of the Control Template with several of the areas collapsed:
First, we have the ControlTemplate tag. The TargetType lets us know what kind of control this applies to. This template can only apply to Buttons. If we were to try to apply it to a TextBox or ListBox, we would get an error. The Key let's us reference this like any other Resource.
Our outer element is a Grid. This is there primarily to hold the other elements; we don't have any Rows or Columns defined at this level.
Inside the Grid is the VisualStateManager. This is how we provide different looks for the States that we mentioned above.
The Border is the first visual element. We'll take a closer look at the details of this in just a moment. Inside the Border, we have Grid for layout purposes. This Grid has both Rows and Columns and contains our ContentPresenter and our Canvas. We'll see more details on these in just moment as well.
Now, let's go through each of these parts. We'll start with the primary elements and then swing back up to the VisualStateManager at the end.
The Button Border
Here is the complete markup for the Border (the outer edge of our Button):
Notice that the BorderBrush property is set to a "TemplateBinding". The TemplateBinding markup extension indicates that this should be bound to one of the main properties of the Button. In this case, we want the BorderBrush to be the same as the Button's "Foreground" property. And remember from our Style, the "Foreground" is set to the "ApplicationTextForeground" by default. But this can be changed. If we change the Button's Foreground property (either in the property inspector or in the markup, then the BorderBrush will change along with it.
The Border Background property is a little bit different. Notice that we have a SolidColorBrush that is set to the TemplateBinding of Background. This let the background color change if the Button Background property is updated. Notice also that we have a x:Name on our brush (ButtonBackgroundBrush). We gave this element a name because we will use it in our VisualStateManager -- when the State changes, we want the Background to change. We'll come back to this.
The Button Main Layout Grid
The next element is the Grid which has our main layout for the Button:
Let's look at our button again:
The Grid defines where we will place our elements. The first Grid Row/Column contains the "ContentPresenter". The ContentPresenter is responsible for displaying whatever is in the "Content" property in the Button. In our case, the Content is simply text ("Concrete Class"). We use a ColumnSpan of 2 so that the content can stretch the full width of the Button.
Notice that our ContentPresenter does not have any sort of Font information included. Any text that appears in the ContentPresenter automatically picks up the Font information from the Button itself. Since we have all of that information set on the Button Style, we don't need to worry about it here.
As a side note, if we tried to put more than just text into the Content property, our Button would probably behave strangely. This is another area that you need to look into further if you are interested in creating your own button templates that can be used in a variety of situations.
The Canvas is in the second Grid Row/Column and contains our arrow. Since the Row/Column definitions are set to "Auto", this will only be as big as the contents. Since the first column is set to "*", it will take the remaining space. The result is that our icon will be aligned to the bottom right side of our Button.
The Arrow Icon
For the Arrow Icon, we could have used a graphic, but that's generally not the best approach. XAML is designed so that things can be easily resized, stretched, or re-flowed to fill in available space. The best way to make sure that your controls can handle stretching/resizing is to use vector descriptions rather than a .gif or .jpg.
I shamelessly stole this arrow from Laurent Bugnion's blog: 56 Vector Arrows in XAML (isn't the Internet great?). This blog article includes a bunch of different arrows (56) that are all described in XAML paths. I found one that I liked, did a little cutting, and pasted it into my application (then tweaked the colors a bit to fit the style).
I won't show the entire output (since a lot of it is a list of numbers for a Path), but here's the relevant bits:
For the Path statements that make up the arrow and the circle, I made a couple of changes. First, I set the "Fill" property to a TemplateBinding to match the Foreground of the Button. Then, I changed the Opacity to "0.5". This will make the arrow semi-transparent (with the effect of a lighter color). This means that the arrow icon will look lighter than the Button text even though they are the same color.
The "Data" property contains the meat of the path, specifying all of the points in the arrow and circle.
The Visual State Manager
Now that we've see all of the default visual elements, it's time to look at the VisualStateManager. This determines what our Button will do when the various states change. Again, what we have here is very simple; we could make this much more interesting/complex very easily.
Our VisualStateManager has a number of VisualStateGroups. The States that we care about are all in the "CommonStates" group, but the Button also has "FocusStates" and "ValidationStates". The reason there are different groups is to allow for overlap. For example, we could have a button that is both "MouseOver" (from the CommonStates) and "Focused" (from the FocusStates).
Since we only care about the CommonStates, we just have one VisualStateGroup in our Control Template. Inside the group, we have 3 different VisualStates. Each VisualState determines what will happen when the control enters that state. In each of our VisualStates, we have defined a Storyboard with an Animation of Duration zero. This means that we are animating a property (the background color), but the zero duration means we have no "transition" -- the color change happens immediately. If we wanted to be more creative with our transitions, we could add additional animation (such as pulsing when the control is focused).
Our three VisualStates all set the same property. Notice that the Storyboard.Target is set to the ButtonBackgroundBrush. This is the name of the brush in our Border.Background that we saw above. Then we have the Storyboard.TargetProperty which specifies what property we want to set. In this case, we want to set the "Color" of the brush. Finally, we have the "To" that specifies the new value for the property.
For the "Normal" state, we set the color to the "TemplateBinding Background" (which is the default background color we want). For the "MouseOver" state, we set the color to LightSlateGray, and for the "Pressed" state, we set the color to "White". (Note: we would probably want to set these to resource colors or TemplateBindings for a more robust Control Template.)
When we put all of these elements together, we get the visual layout and state-change behavior for our GoButton. The template for the ClearButton is very similar. The primary difference is that instead of using a set of Paths for the icon, it simply uses a large letter "X". Again, for a production application, we would probably want to take a little more time to do a vector graphic. But this works for our simple case.
Putting It All Together
So, now that we've looked at the updated Data Template, Value Converter, Application Brushes, Styles, and Button Control Templates, we can see how these pieces all fit together to form the fresh look of our application:
And remember, we did all of these changes without modifying our Application code -- the application behaves just like it did before (loading in data from a web service and displaying it in a ListBox). And now we have a completely new look with about a day's worth of effort (and a lot of that was experimenting with colors and layout).
XAML is pretty awesome, huh?
Happy Coding!
Old Layout
New Layout
The source code for both of these projects is available here: Jeremy Bytes - Downloads. These applications are in the "Old.UI" and "New.UI" projects respectively.
Application-Level Updates
Several of the updates to the application have to do with the general layout and colors. You can check the XAML for more details on this. The key features are the re-arrangement of the grid (swapping the ListBox and Button panels), removal of the background gradient, and the insertion of a solid background.
As mentioned in Part 1, the resources for the application (brushes, data templates, and value converters) were moved from MainWindow.xaml to the App.xaml Resources section. Let's start by looking at the top of our App.xaml (from New.UI):
The first thing to note is that we have moved our Value Converters from MainWindow.xaml to here. This was necessary because several of the value converters are used in the ListBox Data Template that is also in this file. To bring in the Value Converters, we needed to add the namespace for the local project (so we can access the classes in Converters.cs). For more information on Value Converters and how they get added, please see Introduction to Data Templates and Value Converters in Silverlight (also works in WPF).
The next section contains brushes for our application. There were no resources for these items in the old application. I added them here so that it would be easy to update the application colors in the future (since Metro-ish applications will go out of style sometime in the future). Notice that I named the resources after what they are used for and not what colors they are. If I had named the resource something like "LightGrayBrush", then it would be difficult if we wanted to change it to another color. Since the name describes what the brush is used for (rather than what it looks like), we can change this to blue in the future without worrying about mis-matched names.
These application brushes are tied to the XAML in MainWindow.xaml (such as setting the application background). And, as we'll see, these are also used in our control templates for the buttons.
Let's see what else is in the App.xaml (sections are collapsed to get the overview):
First, we have a Control Template and Style for our "GoButton". We'll be spending quite a bit of time in this detail below. Next, we have a Control Template and Style for our "ClearButton". The buttons differ in the icons (an arrow vs. an X), but they otherwise behave the same.
Next we have 3 different TextBlock styles for "ApplicationText", "ListItemTextStyle", and "ListTextStyle". We saw the ListItemTextStyle and ListTextStyle in use in our Data Template in Part 1. Finally, we have our Data Template for the ListBox. We looked at this in detail in Part 1.
Comparing Buttons
So, what are the differences between our old and new buttons. Let's compare them side-by-side:
Old Button
New Button
One big difference is operation. The old button is only clickable on the part that looks like a button (the "Fetch" part). The new button is clickable anywhere in the rectangle. This makes it much friendlier to touch-enabled applications since it is a much bigger target.
Let's compare the XAML, starting with the old button in MainWindow.xaml (in Old.UI):
Notice that we have a border that encloses a StackPanel. And that StackPanel contains a TextBlock and an actual Button.
Compare this to our new XAML (in MainWindow.xaml in New.UI):
The difference is here we just have a Button with the content of "Concrete Class". So, where's the rest of it (the border and the icon)? That's all part of the custom Control Template. And it's getting applied to this button through the Style property.
The Button Style
As we saw earlier, the GoButtonStyle is in the App.xaml. Now, let's take a look at the details:
The Style allows us to apply settings to properties centrally. We have 2 buttons in our application that use this Style (the "Concrete Class" button and the "Interface" button). But we only have to set these properties once. And if we decide that we want to change something (like the FontSize), we just update it here, and it automatically propagates to all buttons that are using this Style.
Notice that the "Foreground" and "Background" properties are set to our "Application" brush resources that we set above. This is important. We want our buttons to have the same background as the application. If the application background changes, we want our button background to change along with it. (Note: this might not always be the case, but that's the behavior that we want here.)
Finally, we have the "Template" property set to the Control Template that's also in our App.xaml.
The Button Control Template
Before we look at the specifics of the GoButton Template, I want to say a few words about Control Templates.
As mentioned in Part 1, XAML controls are "lookless". This means that the behavior is completely separated from the visual display. We are provided with default templates (so that we don't have to create our own), but the templates are fully customizable and/or replaceable.
Control Templates are generally very complex. They are designed to handle a variety of states. For example, a button has a number of states, including "Normal", "MouseOver", "Pressed", "Disabled" as well as "Focused" and "Unfocused" (in addition to others). If you want to have all of these features available, then it's often easiest to use Expression Blend to export the current control template for you to modify.
In our case, we are only handling a subset of these states (Normal, MouseOver, and Pressed), and we don't worry about the other states (since they aren't really applicable to our application). One thing to note: just because we do not implement a visual change for a State does not mean that that State does not exist. For example, our control template does not implement "Disabled", but our button can still be disabled -- it just won't look any different from an enabled button. Same with "Focused" -- the button itself still supports the idea of "focus", but it will not look any different if it is focused.
If we were creating a set of custom templates to be used more extensively, then we would definitely want to implement all of these states. As it is, we'll just focus on the ones we care about for this application.
Control Template Overview
Our Control Template is more complex than other bits of XAML that we've seen so far, so we'll break this down into several different pieces. First, let's look at an overview of the Control Template with several of the areas collapsed:
First, we have the ControlTemplate tag. The TargetType lets us know what kind of control this applies to. This template can only apply to Buttons. If we were to try to apply it to a TextBox or ListBox, we would get an error. The Key let's us reference this like any other Resource.
Our outer element is a Grid. This is there primarily to hold the other elements; we don't have any Rows or Columns defined at this level.
Inside the Grid is the VisualStateManager. This is how we provide different looks for the States that we mentioned above.
The Border is the first visual element. We'll take a closer look at the details of this in just a moment. Inside the Border, we have Grid for layout purposes. This Grid has both Rows and Columns and contains our ContentPresenter and our Canvas. We'll see more details on these in just moment as well.
Now, let's go through each of these parts. We'll start with the primary elements and then swing back up to the VisualStateManager at the end.
The Button Border
Here is the complete markup for the Border (the outer edge of our Button):
Notice that the BorderBrush property is set to a "TemplateBinding". The TemplateBinding markup extension indicates that this should be bound to one of the main properties of the Button. In this case, we want the BorderBrush to be the same as the Button's "Foreground" property. And remember from our Style, the "Foreground" is set to the "ApplicationTextForeground" by default. But this can be changed. If we change the Button's Foreground property (either in the property inspector or in the markup, then the BorderBrush will change along with it.
The Border Background property is a little bit different. Notice that we have a SolidColorBrush that is set to the TemplateBinding of Background. This let the background color change if the Button Background property is updated. Notice also that we have a x:Name on our brush (ButtonBackgroundBrush). We gave this element a name because we will use it in our VisualStateManager -- when the State changes, we want the Background to change. We'll come back to this.
The Button Main Layout Grid
The next element is the Grid which has our main layout for the Button:
Let's look at our button again:
The Grid defines where we will place our elements. The first Grid Row/Column contains the "ContentPresenter". The ContentPresenter is responsible for displaying whatever is in the "Content" property in the Button. In our case, the Content is simply text ("Concrete Class"). We use a ColumnSpan of 2 so that the content can stretch the full width of the Button.
Notice that our ContentPresenter does not have any sort of Font information included. Any text that appears in the ContentPresenter automatically picks up the Font information from the Button itself. Since we have all of that information set on the Button Style, we don't need to worry about it here.
As a side note, if we tried to put more than just text into the Content property, our Button would probably behave strangely. This is another area that you need to look into further if you are interested in creating your own button templates that can be used in a variety of situations.
The Canvas is in the second Grid Row/Column and contains our arrow. Since the Row/Column definitions are set to "Auto", this will only be as big as the contents. Since the first column is set to "*", it will take the remaining space. The result is that our icon will be aligned to the bottom right side of our Button.
The Arrow Icon
For the Arrow Icon, we could have used a graphic, but that's generally not the best approach. XAML is designed so that things can be easily resized, stretched, or re-flowed to fill in available space. The best way to make sure that your controls can handle stretching/resizing is to use vector descriptions rather than a .gif or .jpg.
I shamelessly stole this arrow from Laurent Bugnion's blog: 56 Vector Arrows in XAML (isn't the Internet great?). This blog article includes a bunch of different arrows (56) that are all described in XAML paths. I found one that I liked, did a little cutting, and pasted it into my application (then tweaked the colors a bit to fit the style).
I won't show the entire output (since a lot of it is a list of numbers for a Path), but here's the relevant bits:
For the Path statements that make up the arrow and the circle, I made a couple of changes. First, I set the "Fill" property to a TemplateBinding to match the Foreground of the Button. Then, I changed the Opacity to "0.5". This will make the arrow semi-transparent (with the effect of a lighter color). This means that the arrow icon will look lighter than the Button text even though they are the same color.
The "Data" property contains the meat of the path, specifying all of the points in the arrow and circle.
The Visual State Manager
Now that we've see all of the default visual elements, it's time to look at the VisualStateManager. This determines what our Button will do when the various states change. Again, what we have here is very simple; we could make this much more interesting/complex very easily.
Our VisualStateManager has a number of VisualStateGroups. The States that we care about are all in the "CommonStates" group, but the Button also has "FocusStates" and "ValidationStates". The reason there are different groups is to allow for overlap. For example, we could have a button that is both "MouseOver" (from the CommonStates) and "Focused" (from the FocusStates).
Since we only care about the CommonStates, we just have one VisualStateGroup in our Control Template. Inside the group, we have 3 different VisualStates. Each VisualState determines what will happen when the control enters that state. In each of our VisualStates, we have defined a Storyboard with an Animation of Duration zero. This means that we are animating a property (the background color), but the zero duration means we have no "transition" -- the color change happens immediately. If we wanted to be more creative with our transitions, we could add additional animation (such as pulsing when the control is focused).
Our three VisualStates all set the same property. Notice that the Storyboard.Target is set to the ButtonBackgroundBrush. This is the name of the brush in our Border.Background that we saw above. Then we have the Storyboard.TargetProperty which specifies what property we want to set. In this case, we want to set the "Color" of the brush. Finally, we have the "To" that specifies the new value for the property.
For the "Normal" state, we set the color to the "TemplateBinding Background" (which is the default background color we want). For the "MouseOver" state, we set the color to LightSlateGray, and for the "Pressed" state, we set the color to "White". (Note: we would probably want to set these to resource colors or TemplateBindings for a more robust Control Template.)
When we put all of these elements together, we get the visual layout and state-change behavior for our GoButton. The template for the ClearButton is very similar. The primary difference is that instead of using a set of Paths for the icon, it simply uses a large letter "X". Again, for a production application, we would probably want to take a little more time to do a vector graphic. But this works for our simple case.
Putting It All Together
So, now that we've looked at the updated Data Template, Value Converter, Application Brushes, Styles, and Button Control Templates, we can see how these pieces all fit together to form the fresh look of our application:
And remember, we did all of these changes without modifying our Application code -- the application behaves just like it did before (loading in data from a web service and displaying it in a ListBox). And now we have a completely new look with about a day's worth of effort (and a lot of that was experimenting with colors and layout).
XAML is pretty awesome, huh?
Happy Coding!
Metrocizing XAML - Part 1: Data Templates
Let's face it: gradients and glassy buttons just aren't "cool" anymore. In a way, it sucks; these applications aren't that old (just a year or two). But now they look dated. And this is what makes XAML awesome. Since XAML is "lookless" (meaning that the visual representation of the controls is separated from the underlying operation), we can rip out an old look and drop in a new one without needing to change our application code. And if we have proper separation of the thematic parts of our application, those updates can be isolated to a single location.
As I mentioned a couple weeks ago, I went through several of my sample projects and "metrocized" them. Since these are XAML solutions (several WPF and one Silverlight), the updates were not complicated. Let's take a look at an "old" and a "new" screen together. These are taken from the IEnumerable, ISaveable, IDontGetIt: Understanding .NET Interfaces samples (specifically the IEnumerable.sln).
These projects are available for download in a combined solution here: Jeremy Bytes - Downloads. The "Old.UI" project contains the old layout; "New.UI" contains the new layout.
XAML is Awesome
The best part of this whole process is that XAML completely separates the visual display from the controls themselves. This gives us the chance to change the way our application looks without having to change the underlying code. And as we go through this example, we'll see just that. We didn't need to change any of the application code. (Note: There is one small change to the Value Converter code; but this was done to put some different colors into the converter.) The rest of the updates are in the XAML itself.
One thing to note: I am not a UX designer. I put together passable user interfaces that are pleasing and functional, but I'm not one of those UI wizards (you know who I'm talking about -- the guys that come up with incredible designs, and you smack yourself on the forehead: "That's so obviously awesome!"). I put together the bulk of the design updates in about half a day (colors and layout). It took me a little longer to iron out some of the kinks in the Control Templates. Once the hard part was done, implementing the changes in the different application was very easy (mostly just replacing XAML in the right places).
We'll be looking at these updates in 2 parts. The first part (this one) will cover the updates to the ListBox -- the one with the Person objects listed. This is primarily concerned with the Data Template used by the ListBox, the Value Converter for the color, and a few other minor updates.
The second part (next time) will cover the updates to the Buttons. Our original application used the standard button look-and-feel. The new application uses a custom control template. With the control template, we control the layout, the design (such as the arrows), and also the display behavior -- although you can't see it in the screenshot, the buttons have different colors when you hover over them or click them. This template is fairly simple (compared to how far you can go with control templates), but it has the effect that I was looking for here. We'll walk through this sample in Part 2.
ListBox Updates
The ListBox itself stays pretty much intact. The primary differences are the placement in the application Grid (on the right instead of the left), and the inclusion of a WrapPanel -- this gives us the ability to show multiple columns in our ListBox. Let's compare our old and new markup to see the changes.
First the old markup (from MainWindow.xaml in Old.UI):
Now the new markup (from MainWindow.xaml in New.UI):
The primary difference is the addition of a WrapPanel. We did this by adding tags for the ListBox.ItemsPanel and the ItemsPanelTemplate. By using the WrapPanel, we are specifying that if we run out of space, to "wrap" the list to another column. The WrapPanel has an Orientation property to determine whether to wrap vertically or horizontally. "Horizonal" is the default, and so that is the direction we have here.
Normally, a ListBox would just scroll in order to accommodate any items that don't fit on the screen (either horizontally or vertically). Because we have our wrap panel going horizontally, we need to disable to built-in horizontal scrolling of the ListBox (otherwise, it won't actually "wrap" to the next row). This is why we added the "ScrollViewer.HorizontalScrollBarVisibility="Disabled"" attribute: to disable horizontal scrolling.
The items in the screenshots are in the same order: John Koenig, Dylan Hunt, John Crichton, Dave Lister, John Sheridan, Dante Montana, Isaac Gampu. If we look at the new sample, we see that Dylan Hunt (the 2nd item) comes horizontally after John Koenig. Then we "wrap" to the next line for the 3rd and 4th items).
If we wanted to wrap vertically (so the items go down the first column, then down the second column), we would simply set the WrapPanel Orientation to Vertical, and then disable the Vertical scrollbar on the ListBox.
A Note About the WrapPanel
The WrapPanel is a standard control in WPF 4 (Visual Studio 2010). If you are using Silverlight (4 or 5), the WrapPanel is available as a separate download as part of the Silverlight Toolkit. I used this same ListBox layout in a Silverlight 5 application, and it worked just the same as the WPF version.
The ListBox Data Template
So, the updates to the ListBox itself are not very extensive, but the items are displayed completely differently. This is because we are using a separate Data Template to control the layout. This is denoted in our markup by the ItemSource = {StaticResource PersonListTemplate}" attribute.
If you are not familiar with Data Templates, I would highly recommend that you take a look at Introduction to Data Templates and Value Converters in Silverlight (this works the same in WPF). This covers the creation of the Data Template that is used in the "old" application. We'll just be looking at the differences here.
The Old Data Template
First, let's review the old Data Template. This is located in the Resources section of MainWindow.xaml (in Old.UI):
Just a few notes here: first we have a Border that surrounds our entire template. The BorderBrush is databound to the StartDate property of the Person object. This goes through a Value Converter (myDecadeBrushConverter) to turn the date into a brush. The result is the border color of each item is determined by the decade of the StartDate property (different colors for 1970s, 1980s, 1990s, and 2000s).
The rest of the Data Template is described in the article mentioned above. Basically, we have a collection of StackPanels and TextBlocks to layout the data in the format that we want.
Here are the results for Dylan Hunt:
The New Data Template
The new Data Template is a bit more complex. The first thing to note is that it is no longer in the MainPage.xaml file. The Data Template (along with all of our other resources) have been moved to App.xaml. The App.xaml Resources section gives us a place to put resources that are available to our entire application. In this sample, we only have one screen, so we don't get much from sharing. But we do get a big benefit from centralizing all of our "theming". If we want to change the look in the future, we only have this one place to look (rather than in the separate XAML files). Managing Resources is a bigger topic with lots of options (such as creating completely separate resource dictionaries and assemblies). If you're building larger applications or a suite of applications that all share the same "look", then you'll want to check into this further.
The first part of the new Data Template contains the Border element (from App.xaml in New.UI):
The big change here is that we are no longer binding to the BorderBrush property, we are binding to the Background property. This gives us our different color backgrounds for each item. The Value Converter has also been updated a bit (for the new colors and a bit of optimization). We'll take a look at this in just a bit.
Next, since our layout is a bit more complex, we have a Grid to help us layout our controls:
This gives us 3 rows and 2 columns to work with. Then we have the main layout of our controls:
Again, nothing too unusual here: we're just using StackPanels and TextBlocks like we did before, just with a different layout.
The Styles for the items have been broken out (also in App.xaml of New.UI):
This lets us control the Font properties and alignment separately. If we change our minds about these settings, we can just update the Styles, and our controls will pick them up automatically. Note here that we are using "White" text since we have a contrasting background color.
Here's the result for Dylan Hunt:
Updates to the Value Converter
We also made some updates to the DecadeBrushConverter. This converter returns a Brush object based on the decade of a DateTime property. Our old converter controlled the Border color; the new converter controls the Background color.
Let's look at our original converter (in Converters.cs in Old.UI):
The original Value Converter used a series of "if" statements to determine whether the DateTime value was part of a particular decade. If so, then it returned a SolidColorBrush with an appropriate color.
The new converter has been refactored just a little bit. Here's the new code (in Converters.cs in New.UI):
The biggest functional difference is in the colors that are returned. Rather than being the primary colors of the old border, we have selected more "Metroid" colors for the background.
The change from the series of "if" statements to a "switch" statement was made due to cyclomatic complexity. If you have Visual Studio 2010 Ultimate, you can calculate the Code Metrics for a project (under the "Analyze" menu). One of the items is the Cyclomatic Complexity. This value is like golf scores: smaller is better. The cyclomatic complexity basically tells how many different possible code paths exist in the code.
With the series of "if" statement, the cyclomatic complexity was increased because we have 2 conditions in each "if" (the year is greater than or equal to one value and less than another value). In our refactored code, we calculate the decade before running through the decision process (by doing integer math, when we divide by 10 we lose the last digit; when we multiply by 10 we add a "0" back on -- this gives us the decade). Since we have the decade already, we can use a "switch" statement which only has 1 condition for each item (instead of 2). This reduces our cyclomatic complexity even though our ultimate output is exactly the same.
As a bigger benefit, I think the second version is easier to read (but that's just a personal preference).
Changing the Look with XAML
So, we went from this:
to this:
without changing any of our application code. All we had to do was update our XAML and our Value Converter. That's pretty cool.
Next Time
Today, we looked at how we can update XAML Data Templates to give us a completely different look to our ListBox -- without changing the application code at all.
Next time, we'll look at how I created the control templates that are used for the buttons. The old solution just used the default button templates, but we'll be able to see the changes from a combination of border, text, and button to a single custom-templated button that matches our Metro-ish style.
Happy Coding!
As I mentioned a couple weeks ago, I went through several of my sample projects and "metrocized" them. Since these are XAML solutions (several WPF and one Silverlight), the updates were not complicated. Let's take a look at an "old" and a "new" screen together. These are taken from the IEnumerable, ISaveable, IDontGetIt: Understanding .NET Interfaces samples (specifically the IEnumerable.sln).
Old Layout
New Layout
These projects are available for download in a combined solution here: Jeremy Bytes - Downloads. The "Old.UI" project contains the old layout; "New.UI" contains the new layout.
XAML is Awesome
The best part of this whole process is that XAML completely separates the visual display from the controls themselves. This gives us the chance to change the way our application looks without having to change the underlying code. And as we go through this example, we'll see just that. We didn't need to change any of the application code. (Note: There is one small change to the Value Converter code; but this was done to put some different colors into the converter.) The rest of the updates are in the XAML itself.
One thing to note: I am not a UX designer. I put together passable user interfaces that are pleasing and functional, but I'm not one of those UI wizards (you know who I'm talking about -- the guys that come up with incredible designs, and you smack yourself on the forehead: "That's so obviously awesome!"). I put together the bulk of the design updates in about half a day (colors and layout). It took me a little longer to iron out some of the kinks in the Control Templates. Once the hard part was done, implementing the changes in the different application was very easy (mostly just replacing XAML in the right places).
We'll be looking at these updates in 2 parts. The first part (this one) will cover the updates to the ListBox -- the one with the Person objects listed. This is primarily concerned with the Data Template used by the ListBox, the Value Converter for the color, and a few other minor updates.
The second part (next time) will cover the updates to the Buttons. Our original application used the standard button look-and-feel. The new application uses a custom control template. With the control template, we control the layout, the design (such as the arrows), and also the display behavior -- although you can't see it in the screenshot, the buttons have different colors when you hover over them or click them. This template is fairly simple (compared to how far you can go with control templates), but it has the effect that I was looking for here. We'll walk through this sample in Part 2.
ListBox Updates
The ListBox itself stays pretty much intact. The primary differences are the placement in the application Grid (on the right instead of the left), and the inclusion of a WrapPanel -- this gives us the ability to show multiple columns in our ListBox. Let's compare our old and new markup to see the changes.
First the old markup (from MainWindow.xaml in Old.UI):
Now the new markup (from MainWindow.xaml in New.UI):
The primary difference is the addition of a WrapPanel. We did this by adding tags for the ListBox.ItemsPanel and the ItemsPanelTemplate. By using the WrapPanel, we are specifying that if we run out of space, to "wrap" the list to another column. The WrapPanel has an Orientation property to determine whether to wrap vertically or horizontally. "Horizonal" is the default, and so that is the direction we have here.
Normally, a ListBox would just scroll in order to accommodate any items that don't fit on the screen (either horizontally or vertically). Because we have our wrap panel going horizontally, we need to disable to built-in horizontal scrolling of the ListBox (otherwise, it won't actually "wrap" to the next row). This is why we added the "ScrollViewer.HorizontalScrollBarVisibility="Disabled"" attribute: to disable horizontal scrolling.
The items in the screenshots are in the same order: John Koenig, Dylan Hunt, John Crichton, Dave Lister, John Sheridan, Dante Montana, Isaac Gampu. If we look at the new sample, we see that Dylan Hunt (the 2nd item) comes horizontally after John Koenig. Then we "wrap" to the next line for the 3rd and 4th items).
If we wanted to wrap vertically (so the items go down the first column, then down the second column), we would simply set the WrapPanel Orientation to Vertical, and then disable the Vertical scrollbar on the ListBox.
A Note About the WrapPanel
The WrapPanel is a standard control in WPF 4 (Visual Studio 2010). If you are using Silverlight (4 or 5), the WrapPanel is available as a separate download as part of the Silverlight Toolkit. I used this same ListBox layout in a Silverlight 5 application, and it worked just the same as the WPF version.
The ListBox Data Template
So, the updates to the ListBox itself are not very extensive, but the items are displayed completely differently. This is because we are using a separate Data Template to control the layout. This is denoted in our markup by the ItemSource = {StaticResource PersonListTemplate}" attribute.
If you are not familiar with Data Templates, I would highly recommend that you take a look at Introduction to Data Templates and Value Converters in Silverlight (this works the same in WPF). This covers the creation of the Data Template that is used in the "old" application. We'll just be looking at the differences here.
The Old Data Template
First, let's review the old Data Template. This is located in the Resources section of MainWindow.xaml (in Old.UI):
Just a few notes here: first we have a Border that surrounds our entire template. The BorderBrush is databound to the StartDate property of the Person object. This goes through a Value Converter (myDecadeBrushConverter) to turn the date into a brush. The result is the border color of each item is determined by the decade of the StartDate property (different colors for 1970s, 1980s, 1990s, and 2000s).
The rest of the Data Template is described in the article mentioned above. Basically, we have a collection of StackPanels and TextBlocks to layout the data in the format that we want.
Here are the results for Dylan Hunt:
The New Data Template
The new Data Template is a bit more complex. The first thing to note is that it is no longer in the MainPage.xaml file. The Data Template (along with all of our other resources) have been moved to App.xaml. The App.xaml Resources section gives us a place to put resources that are available to our entire application. In this sample, we only have one screen, so we don't get much from sharing. But we do get a big benefit from centralizing all of our "theming". If we want to change the look in the future, we only have this one place to look (rather than in the separate XAML files). Managing Resources is a bigger topic with lots of options (such as creating completely separate resource dictionaries and assemblies). If you're building larger applications or a suite of applications that all share the same "look", then you'll want to check into this further.
The first part of the new Data Template contains the Border element (from App.xaml in New.UI):
The big change here is that we are no longer binding to the BorderBrush property, we are binding to the Background property. This gives us our different color backgrounds for each item. The Value Converter has also been updated a bit (for the new colors and a bit of optimization). We'll take a look at this in just a bit.
Next, since our layout is a bit more complex, we have a Grid to help us layout our controls:
This gives us 3 rows and 2 columns to work with. Then we have the main layout of our controls:
Again, nothing too unusual here: we're just using StackPanels and TextBlocks like we did before, just with a different layout.
The Styles for the items have been broken out (also in App.xaml of New.UI):
This lets us control the Font properties and alignment separately. If we change our minds about these settings, we can just update the Styles, and our controls will pick them up automatically. Note here that we are using "White" text since we have a contrasting background color.
Here's the result for Dylan Hunt:
Updates to the Value Converter
We also made some updates to the DecadeBrushConverter. This converter returns a Brush object based on the decade of a DateTime property. Our old converter controlled the Border color; the new converter controls the Background color.
Let's look at our original converter (in Converters.cs in Old.UI):
The original Value Converter used a series of "if" statements to determine whether the DateTime value was part of a particular decade. If so, then it returned a SolidColorBrush with an appropriate color.
The new converter has been refactored just a little bit. Here's the new code (in Converters.cs in New.UI):
The biggest functional difference is in the colors that are returned. Rather than being the primary colors of the old border, we have selected more "Metroid" colors for the background.
The change from the series of "if" statements to a "switch" statement was made due to cyclomatic complexity. If you have Visual Studio 2010 Ultimate, you can calculate the Code Metrics for a project (under the "Analyze" menu). One of the items is the Cyclomatic Complexity. This value is like golf scores: smaller is better. The cyclomatic complexity basically tells how many different possible code paths exist in the code.
With the series of "if" statement, the cyclomatic complexity was increased because we have 2 conditions in each "if" (the year is greater than or equal to one value and less than another value). In our refactored code, we calculate the decade before running through the decision process (by doing integer math, when we divide by 10 we lose the last digit; when we multiply by 10 we add a "0" back on -- this gives us the decade). Since we have the decade already, we can use a "switch" statement which only has 1 condition for each item (instead of 2). This reduces our cyclomatic complexity even though our ultimate output is exactly the same.
As a bigger benefit, I think the second version is easier to read (but that's just a personal preference).
Changing the Look with XAML
So, we went from this:
to this:
without changing any of our application code. All we had to do was update our XAML and our Value Converter. That's pretty cool.
Next Time
Today, we looked at how we can update XAML Data Templates to give us a completely different look to our ListBox -- without changing the application code at all.
Next time, we'll look at how I created the control templates that are used for the buttons. The old solution just used the default button templates, but we'll be able to see the changes from a combination of border, text, and button to a single custom-templated button that matches our Metro-ish style.
Happy Coding!
Subscribe to:
Posts (Atom)





























