XAML Organization

We’ve worked on countless WPF and Silverlight projects over the past several years and throughout that time, we’ve refined both our process and the organization of our solutions. We pass off front-end code to our client developers. So clean, understandable organization is extremely important for an effective transfer of knowledge. Most of the development teams that we work with are well versed with c# code, programming methodologies, and best practices. But typically we find that XAML is not something that they care much about. Due to this lack of interest folks tend to not put much up-front thought into how their Resource Dictionaries are organized, nor is there much in the way of guidance from Microsoft. So then 3 months into their development cycle the application looks great from a code perspective, especially if the Model View ViewModel (MVVM) paradigm is followed, but digging into the various styles, templates, brush resources, etc… reveal a lot of problems.

Most of the problems are related to maintainence. Poor organization within an application makes it difficult and time consuming to maintain. When developers have difficulty finding the source of a problem, they tend to kludge a solution. So it is important to give these developers a clear organizational paradigm to help avoid these kludges.

What follows is a look into the organization we use and why. Please keep in mind that we alter this organization slightly with every client. No project needs are exactly the same.

Basic organization:

XAMLOrganization01
XAMLOrganization01

Some Rules:

Rule#1: One Style per Resource Dictionary

For all but the smallest, apps, you will want to follow this rule. It is much easier to find a style within a directory than if it is buried deep inside of a gigantic “catch-all” Resource Dictionary. It is very similar to the c# rule of one class per file. Now there are exceptions to rule #1, we do occasionally package up more than one style in a Resource Dictionary, but only if there is a tight pairing between styles such as a small supporting style that the main style needs. A good example of this is the button style that a scrollBar uses for “Line up”/”Line Down” buttons. We would package these styles together.

Rule #2: Resource Dictionary named to match contained style

Simple enough, if you have a named style called “HelpButtonStyle”, name the Resource Directory “HelpButtonStyle.xaml”. If you have a nameless style that acts as the default for a control, name the Resource Dictionary the same as the TargetType, for example “ComboBoxStyle.xaml”

Rule #3: No resources directly in App.xaml (only ResourceDirectory merging)

All of your resources (Styles, Brushes, templates, etc..) should live in the directory structure listed above. App.xaml should only be merging those Resource Dictionaries. Again, there are exceptions, but keep it to an absolute minimum.

Rule #4: No Styles or Brushes defined in “Views”

Keep the views as clean and readable as possible. We will often place DataTemplates that are used within a DataGrid or ItemsControl in the Resources section of a View since DataTemplates are most often very specific to a view. Also, DataTemplates are part of the overall implementation and not just for styling.

Now that we’ve identified some rules, lets take a closer look at the Directories and Files:

We haven’t yet talked about Brushes and best practices for dealing with them. I have another blog post planned for talking about those challenges. So for the time being, understand that we typically abstract all colors and gradients out of the views and the Styles. We place these in a Resource Dictionary called DefaultColorTheme.xaml. There are several benefits to this approach including performance benefits along with the ability to have multiple color themes for your application, such as Dark and Light themes.

Where the entire directory structure can live

We like to create a project within the solution to contain the Resource Dictionaries and the CustomControls that are used in an application. We will call this Project “ControlLibrary”, but often it has something prepended to it which is determined by the needs of our clients.

CoolApp.Main would have a reference to CoolApp.ControlLibrary which would take care of making the classes available to the rest of the application. But we still need to handle merging the Resource Dictionaries into the Application Resources. This is where the mysterious “ResourceLibrary.xaml” comes into play.

The ResourceLibrary.xaml file merges all of the other Resource Dictionaries into one file. You can use relative paths here. Overall, this keeps the ControlLibrary’s Resources very encapsulated. So within the App.xaml file of the Main application all we have to do is merge the one “ResourceLibrary.xaml” Resource Dictionary.

App.xaml:

Unfortunately, there is a bug/optimization in Resource Dictionary merging that requires a work-around to make the above function:

The full history of the bug can be found here. Microsoft has described it as follows:

“As you can imagine there are several performance optimizations to make that (implied) lookup a light weight as possible. One of them is that we don’t look inside Resource Dictionaries unless they are flagged as “containing default Styles”. There is a bug: if all your default styles are nested in merged dictionaries three levels deep (or deeper) the top dictionary does not get flagged so the search skips it. The work around is to put a default Style to something, anything, <Style TargetType={x:Dummy} /> in the root Dictionary.”

If you have other resources within the App.xaml file, then the workaround is not needed. So typically you will just have to put the workaround in place for the early days the development, then remove it sometime down the line.

Parting Notes

The overall effect here is that all resources are available to all aspects of the application right from the get-go. This is typically a fine way of doing it, but one could argue that there may be some performance benefits to loading resources more strategically. WPF and Silverlight have a lot of optimizations in place that happen behind the scenes with respect to resources. So it is extremely likely that as you try to load things more strategically, you actually introduce performance issues. The most important thing that you can keep in mind is to:

Reuse Styles, don’t duplicate them.

There are a lot of tools in place for making a style work for multiple scenarios, such as TemplateBinding with smart defaults and property triggers. Before duplicating, exhaust these options first. However always keep a holistic view of your application and architect the resources accordingly.

Jeff McLean