Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactoring a large, complex user-interface

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?

like image 916
Martijn Avatar asked Jun 25 '11 08:06

Martijn


People also ask

What is user interface refactoring?

User Interface Refactoring: A simple change to the UI retains its semantics such as (Align entry field, Apply common button size, Apply font, Indicate format, Reword in active voice and Increase color contrast) It delivers consistency for all users -both within your organization and your customers organization.

What are the best practices for refactoring?

The best time for refactoring is before adding new features or updates to existing code. Doing so can help improve the product's quality. By cleaning the code base before adding new features or updates, it helps to make the product more robust and easier to use in the future.

Does refactoring improve performance of a program?

Refactoring may lessen performance, but the change may be negligible. You need to balance the change in the maintainability of the code vs. the change in its performance.


1 Answers

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);
        }
    }
}
like image 151
Neil Barnwell Avatar answered Sep 29 '22 16:09

Neil Barnwell