Search code examples
.netwinformsdesign-patternsmvvmseparation-of-concerns

Path for separating out the view layer from an existing winforms app?


Given:

  1. Our organization has a standard windows form application
  2. The form and business logic are intertwined -- i.e. the Autonomous View. We know that the Autonomous View pattern makes writing unit tests hard. The goal is to take a single form and to decouple the presentation logic from the form itself so that we have a stand alone domain entity/object that can more easily have unit tests written against it.
  3. We are NOT doing a complete rewrite. I am looking at a gradual approach that allows one screen to be dealt with in isolation.
  4. After some research I think the Presentation Model pattern or the MVVM pattern are most appropriate for the way this organization does things.
  5. This organization generally prefers fewer layers of indirection, thus simpler (even though less robust) is better.
  6. The transition from what was to what will be should be formulaic (easy to teach any developer already familiar with in-house concepts). That's why they want to stick with Windows forms over other newer technologies like WPF.
  7. The domain model will know nothing of the view (form). The form will be completely aware of changes to the domain model. That makes 2 layers of indirection. 1 more would be fine (that's why I would allow for MVVM).

Most of the MVVM examples I've found illustrate how it fits together with WPF, not with plain old Windows forms.

Two questions:

Given all this, is there anything I stated that has you thinking I'm not on a good path or that I am on the wrong track? I'm looking to make some recommendations to management.

Finally, do you know of a good online code example that would help me flesh out a prototype?


Solution

  • I hate to be one to answer my own question but additional research and experimentation has illuminated things a bit.

    The ideal solution would be one where the extraction of a domain object would itself be very similar in nature to the design practices used at the organization. Essentially, it would have been an extraction of a visual form in all of its UI glory, to a more abstract form ("VM" or view-model) that embodies the concepts used on the forms (combos, buttons, etc.) without using actual user controls. The VM would then be bound to the view where the controls are actually found.

    Unfortunately, this is an arduous endeavor where the abstractions would have to be robust enough to behave in a similar manner with the controls to which they map. Being that our organization has dozens and dozens of custom controls, that is no short order.

    Take a simple in-house combo box for example. A combo represents an input with a restricted set of choice options. Our data model is an in-memory dataset. The combo binds to a particular table and as the user fills in the form and thus provides criteria his permissible selections are further filtered. That combo displays a value to the user (DisplayMember) and persists another to the DB (ValueMember). There's probably a bit more to it than that; however, the "field" abstraction for a combo has to account for some of these concepts so that it can ultimately be properly mapped to the view. Furthermore our form subscribes to a host of events originating from its controls. These would have to be mapped in some fashion. So it seems to me that a great deal of form (view) functionality has to have a counterpart in the VM. The VM thus becomes to some degree a shadow of the physical form it represents.

    I originally thought it might be possible to extract a very general abstraction of a form and its constituent controls and then to map that abstraction (which closely resembles the original form) to the view. While it may be possible, I now believe it is impractical and may itself present a wide array of new issues.

    So while I wanted to avoid having tests run against an actual form instance, I think this may be better that trying to add a layer of abstraction. Building that abstraction would be similar in effort to rewriting the our screens using another paradigm and thus keeping none of the original shape of the screen. That is, even as we might rewrite one form at a time thus allowing a gradual transition, the final version would not be reusing as much of the original structure as would have been hoped.