Dear Dr. WPF,
I am about to begin a rather large WPF project. I’ve worked on plenty of development efforts involving Windows Forms projects and even a few Win32 projects, but this will be my first foray into WPF. Can you give me general guidance on how to best structure a Visual Studio solution that is targeting WPF?
When people are first getting started with WPF, they often feel more than just a little intimidated. The platform is so big and something as basic as project structure may seem overwhelming. If you find yourself in this position, my advice is to charge ahead blindly! Don’t let the unknowns prevent you from making immediate progress.
I’m happy to share some things I’ve learned on this subject…
Just to be clear, I’m not referring to application architecture in this post. (However, I’m more than willing to expound on that topic too. But if you’re just starting, you would probably find such a discussion mind numbing at this point. If you really care, let’s just say that I tend to follow an architecture similar to the model-view-viewmodel approach described by John Gossman. I never had a name for it until he blogged about it, and I still don’t have a name for it. I think his chosen term suffers from the same branding problems as the WPF platform itself, so I refuse to call it m-v-vm. )
In this discussion, I’m talking only about how you structure and manage the projects, files, resources, etc, within your Visual Studio/Expression Blend solution. I believe this is what you are asking about, correct?
The good news is that it’s fairly simple to change your structure along the way as long as you regularly revisit the topic and keep things heading in the right general direction.
Is there a right way to structure a WPF solution?
What is the right way to structure a WPF solution?
There is not one.
Okay, before you accuse me of contradicting myself, let me just clarify that this is an exercise in semantics and these two questions are actually quite different. (If everyone could please pull out their high school grammar books, we’ll continue…) In the first question, the indefinite article “a” is used (“a right way”) so the question is asking if there is at least one right way. In the second question, the definite article “the” is used (“the right way”) to effectively ask for the one and only right way.
My whole point here is that you shouldn’t get caught up thinking there is only one correct way to structure your solution. There is definitely not a one-size-fits-all structure for WPF solutions. The general rule is that the project, itself, should determine the project structure. WPF is no different than any of the previous platforms you’ve targeted in this respect.
Having said that, I definitely think there are some patterns that can be followed to make WPF solutions more manageable, sensible, and approachable for the developers and designers working on the project.
Use Project Folders
This one goes without saying… but I’m saying it anyway. Good project structure begins by organizing project files in a meaningful way. Unless we’re talking about an extremely simple project with one or two windows or navigation pages, you should be leveraging folders within your project to keep files logically organized. My projects typically have at least a rudimentary set of root folders, as shown in the Solution Explorer image to the right.
Organize by Resource Type
WPF applications tend to have many different types of resources. For large applications, it makes sense to use separate subfolders for common types of resources. In most of my projects, you will at least find a root project folder named Resources. Within there, you are likely to find additional folders with names like Images, Media, Styles, Converters, CultureStrings, etc. Some of these folders (Images, Media) are likely to contain embedded binary files, whereas others (Styles, Converters, CultureStrings) are likely to contain resource dictionaries for application resources. The markup for the application class will typically contain references that merge in the appropriate application-wide resources.
<Application x:Class="StructureMatters.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:src="clr-namespace:StructureMatters" Startup="ApplicationStartup"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/presentationframework.aero;component/themes/aero.normalcolor.xaml" /> <ResourceDictionary Source="/StructureMatters;component/Resources/Converters/ValueConverters.xaml"/> <ResourceDictionary Source="/StructureMatters;component/Resources/Icons/CommonGlyphs.xaml"/> <ResourceDictionary Source="/StructureMatters;component/Resources/Icons/CustomCursors.xaml"/> <ResourceDictionary Source="/StructureMatters;component/Resources/Styles/NativeControlStyles.xaml"/> <ResourceDictionary Source="/StructureMatters;component/Resources/Styles/EditModeControlStyles.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
The code-behind for the application class may selectively merge in additional resource dictionaries. For example, if the application needs to support dynamic locale switching, the appropriate UI culture resources will be merged into the application resources at startup. A different dictionary of localized resources may later be swapped in if the user changes locale.
Organize Projects by Usage Scenarios
If a class is going to provide functionality that will be needed in other projects outside of your current application, go ahead and create a separate project for it within your solution and build it into a separate assembly. Use project references to ensure that the correct dependencies are created between the different projects in your solution. In this area, Visual Studio makes it very easy to develop solutions that share code across multiple assemblies.
Leverage the Object-Oriented Nature of the Platform
Hey, did you know that WPF is built on an object-oriented framework called .NET? I’m sure you did Sacha. I’m being a bit facetious here because a good deal of code I’ve seen recently leads me to believe that this concept is foreign to a lot of people. (I plan to do a separate blog on some of my pet peeves in this particular area. For some reason, perfectly good OO developers seem to toss out a lot of their OO skills when they begin writing WPF code.)
The reason I bring this object-oriented notion up here in a discussion on project structure is because the arrangement of class files within a solution should typically be related to the actual class hierarchies contained therein.
If I’m looking at a class that provides data access functionality, it should be located alongside the other classes that provide similar functionality. Furthermore, if they can leverage shared base functionality, data access classes should derive from a common base class. The base class files should be located in a logical location in relationship to their derived classes (such as alongside them or maybe in a primitives directory, assuming the base classes are not expected to be instantiated directly).
Use Meaningful File Names and Namespaces
My recommendation is that a single code file should never contain more than one class (unless we’re talking about a nested class). Furthermore, the filename of that code file should exactly match the name of its contained class.
If code files are arranged within project folders, the folder hierarchy should match the namespace hierarchy for the contained classes. For example, a C# class named BetterVisualTreeHelper within the DrWPF.Windows.Media namespace should be found within a file named BetterVisualTreeHelper.cs located within the folder path DrWPF\Windows\Media.
Visual Studio will do its best to assist you in this effort by creating default namespaces for new code files that match your project’s folder structure.
Keep Your XAML Files Editable in Blend
If you have skilled designers working on the project, they will probably be far more productive when they can edit the UI files directly in Blend. In version 1.0 of Blend, there are still a few things that will cause a XAML file to fail to load within the Blend design surface. These are usually small things that can be avoided or worked around. Make sure that your designers are regularly opening the XAML files in Blend because it is much easier to isolate what caused a failure if you discover the failure soon after the offending change. I’d love to provide an exhaustive list of the things to avoid, but such a list would very quickly become outdated, so I’ll just recommend going to the Blend/WPF forums when you have questions about markup that won’t open in Blend.
The same guidance can also be offered for the VS 2008 designer (a.k.a., Cider). Unlike Blend, this product is still in Beta so expect to find more problematic areas at this stage.
The good news is that with each new pre-release of Blend 2.0 and Cider, there are fewer and fewer compatibility issues. Soon, this whole topic will hopefully become a non-issue.
Revalidate Structure Regularly
Every project evolves. I said it up front and I’ll say it again now. You should revisit your project structure on a regular basis. If you do this, you can make changes along the way to accommodate the changing nature of the project.
Now Get Started
Your project cannot even begin to take its proper structure until you begin to structure your project. So go ahead and get started… just plan to make changes in structure whenever they become necessary.
I’m sure I’ve left out some obvious things here, so I welcome others out there to chime in with your recommendations on project structure.