Chapter 24. Best Practices

What You’ll Learn in This Hour:

  • Application design and organization

  • Layout practices

  • Proper use of Styles, Templates and Resources

  • Code and XAML conventions

If you’ve made it this far, you now have the knowledge and experience to tackle most WPF development projects. WPF is a huge framework, so you’ll want to continue to seek out new information and techniques. As you do this, the one important thing you cannot do without is a firm foundation of good development practices. We’ve tried to provide this type of advice throughout the book, but this topic is so critical that we wanted to devote an entire hour to reviewing and expanding on these ideas.

Application Design

Writing high-quality software is hard—very hard. Often the vision you have at the beginning of a project does not exactly correspond with the product you end up with. Over the course of development, requirements often shift, either because of changes in corporate policies or new discoveries surrounding what users need to do their jobs more efficiently. These changes often leave developers with a hacked code base that is difficult to understand or maintain. Because the changing nature of an application is almost unavoidable, we’ll make a few recommendations that will help mitigate the troubles and ensure a solid application design.

Orthogonality

Writing orthogonal code is the main goal of several of the principles we are going to mention. But what does this term mean? The term itself has origins in the geometric world, where it means “lying in right angles.” Over time, however, this word has come to describe “things that are independent of one another.” This latter definition more accurately applies to software design.

As you look over the code in a typical software solution, you’ll find many different “categories” of code. You will typically have UI code (the primary subject of this book), business logic, data access, and other infrastructural concerns, such as logging and authentication. If you think over the previous list, you’ll realize that these items are orthogonal. What does logging have to do with UI code? What does user application authentication have to do with your choice of data persistence (SQL Server vs. flat file)? Nothing. But often, over the course of developing an application, these unrelated bits of code end up mixed together. It happens as a matter of convenience, but what many a developer doesn’t recognize is the pain that it will cause them later.

For example, consider this simple, common scenario: You are writing an application like the Contact Manager for your company. In the process of writing this program, you write code in each of the presenters to track errors by writing details to the application event log. This isn’t a big deal, because there are only two presenters. Your program gets popular and users begin to ask for more features, causing you to create additional presenters and views, as well as extend those already in existence. You consider factoring the logging code out into its own class, but decide not to take the time right now. Several months pass and your application is being used by nearly every employee in your company. Things were fine for the first few versions, but lately users have been complaining of frequent crashes. Your boss is concerned and would like you to get to the bottom of this quickly. He asks you to add email-based error logging to the system. You’re thinking this won’t be a big deal, but then you look at the code base and are reminded that the application now has more than 30 presenters with corresponding views, as well as a number of other classes that are doing logging. Right now, you are wishing that you had refactored to a more orthogonal solution several months ago when it first occurred to you. Now you have five hours of work to do instead of five minutes.

Single Responsibility Principle/Separation of Concerns

If you recognize the value of orthogonality, you’ll want to apply two related principles to your coding: the Single Responsibility Principle (SRP) and Separation of Concerns (SoC). An understanding of these concepts and an ability to practically apply them will go very far in helping you to achieve clean, maintainable code.

The Single Responsibility Principle states “there should never be more than one reason for a class to change.” Taking our earlier example, the presenters in our Contact Manager are exhibiting multiple responsibilities. This is because any given presenter will require code changes if either its presentation logic changes or the logging policy changes. SRP is violated because the class has at least two reasons to change rather than one. To fix this problem, and make our code more orthogonal, we would apply Separation of Concerns (SoC).

SoC embodies a “divide and conquer” approach to programming. Rather than having huge, complex classes, our system should be built from smaller, more focused components that work together. One way to apply SoC to our Contact Manager example would be to create a new class, possibly called Logger. This class could have several overloaded Log methods that enable logging of various types of information. We would then refactor our presenters to use an instance of the Logger rather than writing directly to the event log. In the case that the logging policy changes, all you have to do is change the Logger. Now the Logger has only one reason to change, and so do the presenters, satisfying SRP.

Don’t Repeat Yourself

One of the obvious things wrong with the logging code scenario is the simple, needless repetition of code. One of the most fundamental programming principles, Don’t Repeat Yourself (DRY), says No! There are many ways to avoid repetitious code. You can factor out a helper method, derive a new base class, or apply SRP to factor out a completely new class. The key point is if you find yourself writing the same sort of code over and over again, there’s something wrong. You’re not producing DRY code.

Patterns

As you seek to write more orthogonal code by applying the preceding principles, you’ll find some interesting challenges. It’s not always easy. Fortunately, you’re not stranded. Patterns are cataloged, tested solutions to common coding problems. Many of these patterns have already been implemented in .NET and WPF itself and can be used to improve your own code. If you have not studied design patterns or haven’t been thoroughly exposed to refactoring, we heartily suggest you take a look. Becoming familiar with these tried techniques will improve your code and your ability to quickly create solid, proven solutions to even the most difficult problems.

There are many categories of patterns. You will frequently hear about design patterns, enterprise architecture patterns, refactoring patterns, and several others. These categories are pretty loose, and some patterns can be placed in multiple categories. The most popular category is design patterns. Design patterns are ones that are more applicable to a developer’s everyday work. They address problems common to building object oriented systems.

Watch Out!

When I first began learning design patterns I went out of my way to look for places to use them. Be careful! Don’t invent reasons to use design patterns that you like or are familiar/comfortable with. This adds unnecessary complexity. Instead, just write code. When you begin to see repetition or other “code smells,” then and only then refactor. It is at this point that understanding the patterns helps you to improve already working code.

Inversion of Control/Dependency Injection

When you begin to apply the preceding principles and utilize some of the documented patterns, your code will be transformed. More often than not, instead of having a few monolithic classes, you’ll end up with a lot of very small classes. Each of these classes will have a very specific purpose. To accomplish complex tasks, you will often end up with “manager” or “coordinator” classes that orchestrate the interactions between many smaller classes. These coordinating classes are dependent on the classes they use. Presenters are great examples of coordinating classes. To see a concrete example of this, look back at the Contact Manager application. Examine the ApplicationPresenter. The constructor signature is shown next:

public ApplicationPresenter(Shell view, ContactRepository contactRepository)

Notice that the presenter is dependent on both a Shell view and a ContactRepository. It cannot function without these other components, yet it doesn’t take the responsibility of creating them. This is called Inversion of Control, often abbreviated IoC. The control of dependency creation is inverted because the presenter has given up its right to create the components it depends on. It is relying on some outside source to hand it the pieces it needs to work. These dependencies are then injected into the class via the constructor. This is known as Dependency Injection, or DI, and it adds a great deal of flexibility and extensibility to your software design. Some argue that code isn’t truly object oriented unless its dependencies are inverted.

Ya Aren’t Gonna Need It

Ya Aren’t Gonna Need It, or YAGNI, is one of the simplest principles to understand, but very rarely followed. Simply put, YAGNI says that you should code only what you know you need right now, nothing more. Scores of programmers get carried away trying to think of every possible future need, and they go out of their way to work those things in before they have been demonstrated as necessary. This often causes overly complex code and a host of unused features. If you follow the principles discussed earlier, you end up writing code that is more flexible and easy to maintain and extend. When you do need to add new features, it will be a much easier task.

WPF as an Architecture Example

You shouldn’t be surprised to hear that the architects of WPF used many of the techniques we’ve been discussing. So, understanding them will both help you develop your own software better and understand more deeply how WPF is put together and functions. Let’s look at a few of the many examples:

  • Orthogonality—To state the obvious, WPF is a UI framework. The designers have been careful to keep its implementation within the .NET Framework separate from other portions that are unrelated. It is therefore free to evolve separately from other pieces.

  • DRY—The WPF architects employed many techniques to limit repetition. One of the easiest to spot is in its rich control hierarchy. Think back to Hour 16, “Visualizing Lists,” when we looked at all the controls that are inherited from ItemsControl and ContentControl. Early on, the designers recognized some core features that many controls would posses, and factored them into these two important base classes. Furthermore, they recognized an even broader set of functionality expressed by types such as FrameworkElement and Visual. The application of DRY helped to eliminate a potentially massive amount of repetitive code.

  • Design Patterns—WPF’s architecture is based on many of the most common design patterns in use. To begin, the hierarchical nature in which a UI is built in WPF is a perfect example of the Composite pattern and the XAML parser is a very advanced Factory and Interpreter. The command system in WPF is an obvious implementation of the Command pattern and the Decorator base class is an equally apparent implementation of the Decorator pattern. The application object itself is a Singleton. The list goes on and on.

    By the Way

    Composite, Factory, Interpreter, Command, Decorator, and Singleton are popular design patterns. There is a wealth of information available about these patterns on the Web. Excellent descriptions of these individual patterns are available on Wikipedia.

  • SRP/SoC—If you look at the templated nature of controls in WPF, you’ll realize that a control’s behavior has been separated from its appearance. The architects of WPF recognized this fundamental difference in concerns. They separated them by delegating the responsibility of behavior to the control and of appearance to the control template. Therefore, a control has only one reason to change (SRP)—if its behavior changes. If its appearance needs to change, you just change the control template.

UI Design and Architecture Concerns

The previous design principles can be applied to all aspects of an application. In the sections that follow, we’d also like to give some specific recommendations concerning the building of user interfaces.

Organization

Code organization is very important. You should have a meaningful overall structure to your solution. When it comes to a WPF project, you should think carefully about how you want to organize the various types of classes and resources. We have tried to demonstrate an appropriate WPF project structure of various levels of complexity over the course of this book. In general, if you are using an MVP pattern, you want to group your classes in folders/namespaces that indicate their role. If your project is large enough, the model classes will often be broken out into a separate assembly. You should also keep user controls and custom controls in a special namespace. These can also be put in their own assembly if you find yourself needing to use your custom controls across multiple projects. Generally, you will want to have a dedicated location for resources. Many real-world applications require some embedded graphics or other files. You may want to subdivide the resources folder if you find yourself with a large number of resources. Don’t forget to add your XAML resource dictionaries here as well. Again, if the project is complex enough, you may want to break out your resource dictionaries based on the type of resource and merge them at runtime. Regardless of the organizational strategy you choose, you want your code base to be easily navigable. If you are looking for the EditContactPresenter or the gradientBackgroundBrush, you don’t want to have to think to know where to find it. Strive to make the organization as intuitive as possible.

Model View Presenter (MVP)

It should be obvious by now that we favor the use of an MVP pattern for all but the most trivial of applications. One of the reasons is that it is one of the best examples of SRP/SoC. Designing your UI with this pattern in mind leads to a result that is much easier to maintain and extend than the alternatives. Also, if you want to use the principles of IoC/DI, it might be difficult if all your code is in a code-behind file. It requires a lot of extra work to figure out how to inject dependencies into classes whose construction you cannot control (instances that are created through XAML). These patterns work better with a presenter, where you have complete control over the classes, base classes, and constructors.

By the Way

Although we haven’t mentioned Test Driven Development (TDD) explicitly in this book, many of the principles we have been discussing in this hour make this practice much easier. To that end, if you are writing unit tests for your code, you will find it nearly impossible to test code that exists in a UI code-behind file. This is yet another reason to use the MVP pattern. MVP allows you to more easily unit test the three components. In general, you’ll find an emphasis on testability leads to good software design.

Usability

With WPF as your toolset, you are virtually unlimited with regard to the types of UI you can create. Literally, almost anything you can imagine can be done. Nevertheless, this tremendous power and flexibility make it very easy to go wrong in your design decisions. Remember, the goal of a user interface is to help users get their jobs done quickly and painlessly. You want them to have increased productivity and actually enjoy what they are doing. Sticking with established UI patterns seen in popular software will help you to get a start in the right direction. Whenever you are building a user interface, you should be in constant communication with your users. In this way, they can give you feedback about the UI decisions you are making. If they are utterly bewildered by your design choices and can’t relatively quickly figure out how to do what they need to do, you might want to reconsider the direction you are heading in. Here’s one bit of advice we’ve found to be true in designing user interfaces: less is more.

Technical Considerations

So far, most of our recommendations have been concerned with higher level or more abstract notions about application design. Let’s take a look at some more specific practices surrounding various areas of WPF that developers often have difficulty with.

UI Layout

Over the course of the book, we’ve taken a variety of approaches to handling UI layout. We hope that by now you are fairly comfortable with how to approach this issue. We’d like to restate a few earlier recommendations that may have more meaning to you now:

  • Design your layout using the simplest and most explicit Panel.

  • Do not be afraid to combine multiple Panels to achieve the effect you desire. As you have seen in previous hours, this is the rule rather than the exception.

  • Pay close attention to the runtime behavior of your layout. You may need to change your strategy to accommodate window resizing. Remember, some panels look the same at design time but behave differently at runtime.

  • Choose layout options that allow for flexible sizing. Avoid setting hard-coded Height and Width properties when possible. Instead, if necessary, consider using MinHeight, MinWidth, MaxHeight and MaxWidth.

  • If using a graphical UI tool such as the VS Designer or Expression Blend, keep a close eye on Margin and the other layout-related properties of your elements. Sometimes these tools get confused and alter these values in strange ways, resulting in unexpected layout behavior. It’s often necessary to clean up your XAML after using one of these tools.

  • Use Canvas only as a last resort. This panel was designed primarily for rendering Drawings (such as icons or logos), not UI. Using Canvas for ordinary layout scenarios can defeat the purpose of WPF’s dynamic layout capabilities. If you want a similar effect, use a Grid control in combination with Margin set on its children. This creates a canvas effect in which the positioning is relative.

Resources

Traditional application resources, as well as those stored in a ResourceDictionary, are an important aspect of using WPF successfully. Here are a few bits of advice:

  • Create a designated folder in your project for storing resources. We have mentioned this several times now because it is very important.

  • If you have many resources, consider organizing them in subfolders. For example: Images, Fonts, Styles, ControlTemplates, and so on.

  • Use ResourceDictionary.MergedDictionaries to combine the contents of multiple resource dictionaries into one. This is especially important in the Application.Resources.

  • Pay attention to resources early on in the project. We recommend that you plan for this from the initial solution setup and rigorously enforce the organization. If you let it go by the wayside, you will quickly have an unmanageable solution on your hands.

Styles

Styles are one of the most powerful features of WPF. When used properly, they can greatly decrease repetitive XAML and improve the overall maintainability of the system. Here are a few thoughts:

  • Factor out commonly used components of styles such as colors, brushes, and fonts into a central location. Then merge them into your style resources.

  • Use BasedOn to help eliminate repetition across different styles whose targets share a common base class. Remember, you have to key your styles to use inheritance.

  • If you want a consistent look to your app, regardless of operating system version, you will need to style all the basic controls explicitly. You can do this by creating styles with TargetType without specifying x:Key. Otherwise, the controls will pick up whatever the default control style is for that OS. Remember that a Button looks different on XP than on Vista.

  • Stay on top of the organization of your styles. If you start to get a large resource dictionary, break things out into separate files and merge them at the appropriate places. For example, you may want to separate your default styles from the exceptions and styles for built-in controls from those of custom controls.

Templates

WPF’s templating mechanism is perhaps its most innovative feature. Being able to reuse control behavior while changing the look of the control is a great example of SRP/SoC and is a boon for developers. Here are a few thoughts on how to work wisely with templates:

  • Keep your control templates in a separate resource dictionary from your styles. Use styles to set the template, rather than setting the template on individual elements. This gives you more flexibility with the composition of styles and helps eliminate needless repetition.

  • If you build a custom control, strive to place as much of its look as possible into a control template. This will give future users of your control the flexibility to change the look if they so desire.

  • Remember that control templates and data templates can both be assigned by type or key. Additionally, they can be declared inline or in resources at various levels of the application. When choosing how to apply a template, consider issues of reuse as well as discoverability.

  • Remember to use control templates specifically for changing the core composition of a control. A data template should be used to help a control properly render an unknown data type. Do not build specific data types into control templates.

  • Use TemplateBinding as much as possible in your control templates, and then provide the desired default values in a style. This allows your templates to be more flexible, and other developers can use your controls without having to modify the templates.

Coding Conventions

Coding conventions vary from programmer to programmer. The important thing to remember is that if you are working on a team of two or more people you need to establish conventions about how you write your code so that there will be consistency across the code base, regardless of developer. We would like to make a few loose recommendations.

Code

  • Be explicit in your naming of classes, methods, properties, and so on. Good naming is one of the most important aspects of code authorship. Code with well-named constructs is often self-documenting.

  • Use Pascal casing for properties, methods, and types and Camel casing for other names. Do not use Hungarian notation.

  • Use blank lines to improve readability. Format your code into visual blocks that convey intent.

  • Use a little extra effort to keep your code neatly formatted. You can use the Ctrl+K+D shortcut in Visual Studio to autoformat your code. You can use the Tools, Options, Text Editor, [your language] to select formatting options in VS.

XAML

  • Avoid naming elements without a reason and prefer the x:Name attribute over Name when you do.

  • If you name elements, give them meaningful names. “ListBox1” is not a good name, but “FontList” is.

  • Decide on a naming convention for control template parts and resources.

  • Use a little extra effort to keep your XAML neatly formatted. You can use the Ctrl+K+D shortcut in Visual Studio to autoformat your code. Your team should decide on a XAML formatting convention. You can use the Tools, Options, Text Editor, XAML to select formatting options in VS.

Most of our recommendations about naming and formatting are useful in creating maintainable code. It is not our intent to wax on over this subject. There are entire documents devoted to coding conventions.

Summary

We’ve finally wrapped up our study of WPF. In this hour we took some time to look back over some of the techniques applied throughout the book. We’ve given several strong recommendations that should help you become a better software developer in general, and we tied in many WPF-specific tips and practices that should make your job easier. We hope you have enjoyed learning this new technology and building these applications along with us. You now have the knowledge, experience, and tools to succeed. We wish you the best in your WPF creations!

Q&A

Q.

You mentioned dependency injection containers. What are some of the most popular containers other developers are using?

A.

The three most common containers are Castle project’s Windsor container, Jeremy Miller’s StructureMap, and Spring.NET (a .NET port of the Java Spring framework). Microsoft has also recently developed a container called Unity.

Q.

What are some good resources I can use to learn more about design patterns and the other principles you mentioned?

A.

The classic C++/Smalltalk book on the subject is Design Patterns, written by Gamma, Helm, Johnson, and Vlissides (Addison-Wesley, 1994, 978-0-201-63361-0)—often referred to as the Gang of Four (GoF). There are plenty of .NET books on the same subject as well. Almost any one will do. A related book that examines a different set of patterns is Patterns of Enterprise Application Architecture, (PoEAA) by Martin Fowler (Addison-Wesley, 2002, 978-0-321-12742-6). Martin Fowler has several other books worth reading, as well as extensive materials on his website: www.martinfowler.com/. Finally, I would recommend Agile Principles, Patterns, and Practices in C# by Robert Martin and Micah Martin (Prentice Hall, 2006, 978-0-131-85725-4). This is perhaps the best overall look at these topics and related areas.

Workshop

Quiz

1.

Briefly describe the MVP pattern and explain why it is important.

2.

What is orthogonality and how is it beneficial to your code?

Answers

1.

The MVP pattern is a GUI layer pattern generally composed of three types of objects: models, views, and presenters. The model objects represent business entities. If I were building an e-commerce application, the model might contain types such as customer, order, and product. The views are the actual visual representation of the user interface. In WPF this generally boils down to user controls and windows. Presenters are a special type of class that coordinates views and models. Sometimes they control other presenters as well. This pattern is important because it is a clear and practical application of SRP/SoC. Each class has a single responsibility and works with the other classes to achieve a goal. This separation of concerns keeps classes small and focused, making the UI easier to maintain and extend over time.

2.

Orthogonal code is written in such a way that unrelated components can vary independently. For example, a data access component has nothing to do with a UI component, so a developer should be able to change either or both without affecting the other. Building systems in this way gives the code a longer lifespan because each of its individual components can adapt to changing business or technical needs without breaking the rest of the system.

Activities

  • Go out and build your own WPF applications!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.71.28