erictheavg
erictheavg

Reputation: 350

Separating the view in Visual Studio GUI Applications using Design Patterns

I am working on a little file copying program for my company and it's getting large enough that I'm starting to care about code design (I know, I should have from the beginning...).

Right now my problem is that I want my form class code to be as lean as possible. Based on my research, it sounds like I want to implement Duplicate Observed Data in order to keep the View decoupled from the rest of the program. Are there any tips on how to do this in VS2010/C#? Or are there better ways to do what I want to do?

I'm using Visual Studio 2010, .Net 4.0, C#, and it's a Windows Forms Application.

Suggestions on tutorials, books, or open source examples are welcome.

Edit: I just found this article about the MVP pattern, which is probably relevant to me. But I'd still appreciate input.

Upvotes: 3

Views: 1165

Answers (1)

Kevin McCormick
Kevin McCormick

Reputation: 2398

We've approached this in past projects by applying a modified version of the MVVM pattern (usually associated with WPF) in Windows Forms. There is a lot of boilerplate code, so get your typing fingers ready, but it pays off very well in the long run.

Obviously, this is very long and will probably be overwhelming. Take your time and implement each piece at a time. It is worth noting that in WPF, many of the work below is done for you.

First, we start with three name spaces: (for extra points, put each namespace in a separate assembly, but that is not required.

  • CompanyName.ProjectName.Models - Contains your domain objects (including collection classes).
  • CompanyName.ProjectName.ViewModels - Contains various view model classes (see below)
  • CompanyName.ProjectName.Views - Contains your Forms and UserControls

Creating Domain Classes

  • Start as simple as possible, mostly just properties with getters and setters.
  • For each public property of interest in each class, create matching Changed events. For example, create an event called NameChanged if you have a property called Name. The event should be raised from within the setter every time that property's value changes.
  • Create public methods containing business logic, such as Delete(). These methods should throw exceptions if there is an error. (In more advanced designs, you may wish to have a controller object that contains the business logic.)

Creating ViewModel Classes

A ViewModel class is mostly a designer code-behind class "on steroids", except it doesn't have any knowledge of the actual controls or layout of how it will be used, just behavior. Usually there is one ViewModel for each Model class, but you can also make a ViewModel such as ClassroomView that displays a List<Student>.

  • Create an public, abstract ViewModelBase class that implements IDisposable and INotifyPropertyChanged.
  • Create classes for each domain object you want to visualize, or group of objects you want to visualize. These classes should inherit from ViewModel.
  • Create read-only public properties for display in the view model class that represent what you want to display on the screen. For example, in the PersonViewModel class, if you want to display a full name, create a property called FullName, where the getter could concatenate the FirstName and LastName property of an associated Person model.
  • Each ViewModel class should also have a property that relates to the underlying data model object.
  • Create other read/write public properties for display, such as:
    • bool properties like IsHeadOfHouseHousehold for binding to checkboxes or radio buttons.
    • Color properties like HighlightColor for binding to BackColor/ForeColor/etc of various controls.
    • string properties that allow the underlying data model object to be edited, by binding to textboxes.
  • When the ViewModel object is constructed, it should register for all of the events for the underlying data model object, such as FirstNameChanged. In the eventhandlers, you should raise the PropertyChanged event for any properties in the view model that are affected. For example, the handler for the FirstNameChanged event should raise PropertyChanged for FullName.
  • In the Dispose method, you should unregister from the data model events to prevent memory leaks.
  • Create public methods that represent user actions and commands they can perform, such as Delete(). The methods should be written from a user perspective, so that they can raise dialog boxes to say "Are you sure?" Then they should call methods on the data model object, or some other controller object.

Creating Views

OK, now the easy part:

  • Add your ViewModel objects as project data sources in your solution.
  • Create forms and user controls by laying out the controls you want on-screen. Create TextBoxes, DataGrids, etc. For each property that should be bound to a ViewModel instance, use the (DataBindings) property to bind to your project data sources. As you create databindings, it will automatically create BindingSource components.
  • In your main form, create an initial instance of your data model, load from disk, etc.
  • Where necessary, create instances of the ViewModel classes and set the DataSource property of the appropriate BindingSource. The interface will automatically come to life.
  • For buttons, write the Click event handlers to call the appropriate public method on the ViewModel.

The End Result

  • You have easy to modify data model objects. Changes to the data model won't affect the user interface.
  • You have easy to modify view model objects. If you want to change the program's behavior, the data model doesn't have to change, nor does the control layout. If more than one form or user control uses the same view model, you have consistency between different views in your program.
  • You have easy to modify forms and user controls. If you want to the change the look and feel, you don't have to worry about modifying the program's behavior or data model. The code-behind for each form is tiny.

Upvotes: 2

Related Questions