Building a WPF Application: Part 3
Join the DZone community and get the full member experience.Join For Free
I'm going to back up and bit discuss my overall approach to structuring this WPF project. So far the solution consists of four projects:
- ChumChase is the actual WPF project.
- FriendFeed is the official .NET api I downloaded from here, and patched so that it would compile. :-)
- IntegrationTests is for testing the bits of the application that interact with the FriendFeed api. I separated this out because it is much slower to run than my Specifications tests, and goes beyond the trust boundary of the app.
- Specifications is the bulk of the unit tests for the project. I'm influenced by the whole BDD approach, and I like to think of my unit tests as executable technical specifications.
In the main project you'll find the following:
- Presenters - This folder contains the presenter classes. I usually name these classes with the suffix "Presenter, though that's a bit of a hang over from other frameworks (MonoRail, RoR). This directory is where I have the HomePresenter class we've been discussing.
- Views - These are usually user controls or some derivative thereof. I name them to correspond to their presenter. So you would expect, since we have HomePresenter, to find HomeView.xaml. (Actually, I goofed and the current source has the view named HomeFeedView.xaml).
- Model - This is the object model that represents the "business problem" I'm trying to solve. In this case it happens to mirror the actual api quite a bit.
- Repositories - Here I am influenced by Eric Evans' book on Domain-Driven Design. Repositories are classes that allow me to access/persist data in a way that makes sense to my business domain. It's also a place where I can hook in orthogonal concerns such as logging, caching, or security. Repositories differ from the traditional data access layer in that there is an emphasis on the domain model (as opposed to following the semantics of a relation database).
- Services - These are supporting classes. They are not necessarily part of the business or problem domain. A classic example is an SMTP provider. Classes in your model or presenters might utilize these.
- Framework - These support the framework of application, but don't have anything to do with the business concern. If we need to create a base class for views, we'd put it here. Currently, I have a class there that is going to assist us with binding to commands. This is code that could likely be pushed out to a reusable library.
Aside from these folders you will also find:
- App.xaml - This is the starting point for the application. It will kick off our Windsor configuration, and launch the first window for the application. Eventually, we'll include some application-wide resources in the xaml, primarily for styling.
- Shell.xaml - This is the main window of the application. I call it Shell, because there is very little to its UI. It's content is bound to view of our current presenter. This class might have some logic in it's code-behind if we need to do some things like managing a set of tabs. It also provides the overall layout of the application.
- ApplicationController - I briefly mentioned this in the last post. Honestly, I've waffled a bit on what to call it. It's responsibility is to coordinate the presenters for the application. For example, if presenter X needs to load presenter Y it passes that request to the application controller. In this, it also handles some statefulness for the overall application. This will become more clear as we begin to work with the class.
What's Not Here Yet
There are several other things that usually appear in my projects. I often end up with a base class for test fixtures, though I think the new Rhino.Mocks features might affect that. However, the big piece that's missing is the folders and files related to organizing the XAML.
It's common for me to have the following:
- Colors & Brushes - I typically have a resource dictionary dedicated to defining the colors and brushes used in the application.
- Default Styles - When I provide a completely custom style for an application, I'll have a resource dictionary that defines all of the default styles for controls. It useful to note that I avoid putting control templates inline in this dictionary.
- Control Templates folder - In this folder, I will have a file for each control template in the application. I'll usually have a "manifest" resource dictionary where I merge in all of the individual templates. This helps to keep the structure clean in other areas. These are the control templates that are referenced in my default styles dictionary.
- Data Templates folder - Inside the Views directory, I will have a sub-folder for data templates. Data templates really are views.
What Happens at Runtime?
A synopsis of what happens at runtime, well, what will happen once we've finished:
- App.xaml starts off.
- In its constructor, we configure our container, which in this case is Windsor. The container is told how to resolve resources, such as where do I find the actual implementation of IFriendFeedProxy.
- Still in the constructor, we then request an instance of IApplicationController, and call StartApplication(). Note that we don't set the StartupUri in the XAML for App.
- StartApplication() might handle some things like loading up user preferences, but mostly it will initialize and open Shell.xaml.
- Somewhere around here we'll request the HomePresenter from the container, and inject into the shell.
- As the user interacts with the application, presenters may be removed and new ones injected (probably into a tab, or new window). The shell's code-behind has the code for actually placing these presenter's views into the layout.
- The presenter will have use commands as properties that can be bound to in there views. These commands are coordinators of individual activities in the application.
I'm certain that parts of this are confusion, but I believe it will make sense as we continue to build the application. As always, your feedback is welcome.
More to come!
Published at DZone with permission of Christopher Bennage, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.