10

I have a big winform with 6 tabs on it, filled with controls. The first tab is the main tab, the other 5 tabs are part of the main tab. In database terms, the other 5 tabs have a reference to the main tab.

As you can imagine, my form is becoming very large and hard to maintain. So my question is, how do you deal with large UI's? How do you handle that?

Neil Barnwell
  • 41,080
  • 29
  • 148
  • 220
Martijn
  • 24,441
  • 60
  • 174
  • 261

3 Answers3

11

Consider your aim before you start. You want to aim for SOLID principles, in my opinion. This means, amongst other things, that a class/method should have a single responsibility. In your case, your form code is probably coordinating UI stuff and business rules/domain methods.

Breaking down into usercontrols is a good way to start. Perhaps in your case each tab would have only one usercontrol, for example. You can then keep the actual form code very simple, loading and populating usercontrols. You should have a Command Processor implementation that these usercontrols can publish/subscribe to, to enable inter-view conversations.

Also, research UI design patterns. M-V-C is very popular and well-established, though difficult to implement in stateful desktop-based apps. This has given rise to M-V-P/passive view and M-V-VM patterns. Personally I go for MVVM but you can end up building a lot of "framework code" when implementing in WinForms if you're not careful - keep it simple.

Also, start thinking in terms of "Tasks" or "Actions" therefore building a task-based UI rather than having what amounts to a create/read/update/delete (CRUD) UI. Consider the object bound to the first tab to be an aggregate root, and have buttons/toolbars/linklabels that users can click on to perform certain tasks. When they do so, they may be navigated to a totally different page that aggregates only the specific fields required to do that job, therefore removing the complexity.

Command Processor

The Command Processor pattern is basically a synchronous publisher/consumer pattern for user-initiated events. A basic (and fairly naive) example is included below.

Essentially what you're trying to achieve with this pattern is to move the actual handling of events from the form itself. The form might still deal with UI issues such as hiding/[dis/en]abling controls, animation, etc, but a clean separation of concerns for the real business logic is what you're aiming for. If you have a rich domain model, the "command handlers" will essentially coordinate calls to methods on the domain model. The command processor itself gives you a useful place to wrap handler methods in transactions or provide AOP-style stuff like auditing and logging, too.

public class UserForm : Form
{
    private ICommandProcessor _commandProcessor;

    public UserForm()
    {
        // Poor-man's IoC, try to avoid this by using an IoC container
        _commandProcessor = new CommandProcessor();
    }

    private void saveUserButton_Click(object sender, EventArgs e)
    {
        _commandProcessor.Process(new SaveUserCommand(GetUserFromFormFields()));
    }
}

public class CommandProcessor : ICommandProcessor
{
    public void Process(object command)
    {
        ICommandHandler[] handlers = FindHandlers(command);

        foreach (ICommandHandler handler in handlers)
        {
            handler.Handle(command);
        }
    }
}
Community
  • 1
  • 1
Neil Barnwell
  • 41,080
  • 29
  • 148
  • 220
  • Thanx, that's a good idea. The Command Processor sounds interesting, but I don't understand it completely. Could explain that a little more with a little (dummy) code? – Martijn Jun 25 '11 at 08:39
  • Wow, thanks for the further explanation and the links! I found the part about the command processor difficult, because I don't understand it completely. What does `GetUserFromFormFields` returns? And how does `FindHandlers` look like? – Martijn Jun 25 '11 at 09:30
  • I don't think this answer needs more code, to be honest. I wrote what's there already in Notepad so more complexity will introduce mistakes and cloud what I was really trying to point out. `GetUserFromFields()` builds a user object from the fields on the form and `FindHandlers()` will look up the appropriate handlers either from a dictionary or an IoC container somehow. The Composite UI Application Block has stuff in like this, but it has a steep learning curve. My example above is much less complex and hopefully should have enough to get you started. – Neil Barnwell Jun 27 '11 at 08:23
  • Another reason I don't want to build this out any further is because I'd actually recommend the MVVM style approach rather than building business objects from controls on forms. This approach is easier to unit-test and means you're separating your concerns better (i.e. the form isn't actually holding data, only providing a view of it). – Neil Barnwell Jun 27 '11 at 08:25
2

The key to handle a large UI is a clean separation of concerns and encapsulation. In my experience, it's best to keep the UI free of data and functionality as much as possible: The Model-View-Controller is a famous (but rather hard to apply) pattern to achieve this.

As the UI tends to get cluttered by the UI code alone it's best to separate all other code from the UI and delegate all things that don't concern the UI directly to other classes (e.g. delegating the handling of user input to controller classes). You could apply this by having a controller class for each tab, but this depends on how complicated each tab is. Maybe it's better to break a single tab down into several controller classes themself and compose them in a single controller class for the tab for easier handling.

I found a variation of the MVC pattern to be useful: The passive view. In this pattern, the view holds nothing more than the hierarchy and state of the UI components. Everything else is delegated to and controlled by controller classes which figure out what to do on user input.

Of course, it also helps to break the UI itself down into well organized and encapusalted components itself.

Michael Klement
  • 3,376
  • 3
  • 30
  • 34
1

I would suggest you to read about the CAB ( Composite UI Application Block ) from Microsoft practice and patterns, which features the following patterns : Command Pattern, Strategy Pattern, MVP Pattern ... etc.

Microsoft Practice and patterns

Composite UI Application Block

Nour
  • 5,252
  • 3
  • 41
  • 66