Organising UI code in .NET forms
I'm someone who has taught myself programming, and haven't had any formal training in .NET programming.
A while back, I started C# in order to develop a GUI program to control sensors, and the project has blossomed. I was just wondering how best to organise the code, particularly UI code, in my forms.
My forms currently are a mess, or at least seem a mess to me.
- I have a constructor which initialises all the parameters and creates events.
- I have a giant State property, which updates the Enabled state of all my form control as users progress开发者_运维百科 through the application (ie: disconnected, connected, setup, scanning) controlled by a States enum.
- I have 3-10 private variables accessed through properties, some of which have side-effects in changing the values of form elements.
- I have a lot of "UpdateXXX" functions to handle UI elements that depend on other UI elements - ie: if a sensor is changed, then change the baud rate drop down list. They are separated into regions
- I have a lot of events calling these Update functions
- I have a background worker which does all the scanning and analysis.
My problem is this seems like a mess, particularly the State property, and is getting unmaintainable. Also, my application logic code and UI code are in the same file and to some degree, intermingled which seems wrong and means I need to do a lot of scrolling to find what I need.
How do you structure your .net forms?
Thanks
There are a number of patterns that help you seperate logic in applications , which results in cleaner and more maintainable code. The MVP pattern is a good one to start with. It is based on defining 3 areas of responisbility i.e. MVP M = Model, V = View, P = Presenter. If you are familiar with using interfaces you will be fine, otherwise that would be a good place to start (review the basic OO priciples: Encapsulation,Abstraction, Polymorphism). The basic principle of the MVP is to put you application logic in the Presenter. The presnter talks to the view (your form) via an interface, and the view calls back to the presenter (i use a interface for this too) when the user interactions with it. The model is the solution's domain object hierarchy which implments buisiness logic and entity relationships.
Most UI patterns (MVP, MCV etc) are trying to do the same things, seperate you concerns. Tghe following is a simple example:
//The view interface
interface IUserDetailsView
{
string Username{set;get;}
string FirstName{get;set;}
string LastName{get;set;}
UserDetailsPresenter Presenter{get;set;}
void DisplayMessage(string message);
}
//The view implmentation //A standard windows form which has text boxes, labels, combos etc , which
class UserDetailsView : Form, IUserDetails
{
public string Username{set{txtUserName.text = value;}get{return txtUserName.text;}}
public string FirstName{set{txtFirstName.text = value;}get{return txtFirstName.text;}}
public string LastName{set{txtLastName.text = value;}get{return txtLastName.text;}}
Public UserDetailsPresenter Presenter{get;set;}
public void DisplayMaessage(string message)
{
MessageBox.Show(message);
}
private void saveButton_Click(object sender, EventArgs e)
{
Presenter.SaveUserDetails();
}
}
//Presentation Logic
class Presenter UserDetailsPresenter {
//Constructor
public userDetailsPresenter(IUserDetailsView view)
{
//Hold a reference to the view interface and set the view's presnter
_view = view;
_view.Presenter = this;
}
private IUserDetailsView _view;
DisplayUser(string userName)
{
//Get the user from some service ...
UserDetails details = service.GetUser(userName);
//Display the data vioa the interface
_view.UserName = details.UserName;
_view.FirstName = details.FirstName;
_view.LastName = details.LastName;
}
public void SaveUserDetails()
{
//Get the user dryaiols from the view (i.e. the screen
UserDetails details = new UserDetails();
details.UserName = _view.UserName;
details.FirstName = _view.FirstName;
details.LastName = _view.LastName;
//Apply some business logic here (via the model)
if(!details.IsValidUserDetails())
{
_view.DisplayMessage("Some detail outlining the issues");
return;
}
//Call out to some service to save the data
service.UpdateUser(details);
}
}
//Finally, the model
public class UserDetails
{
public UserName {get;set;}
public FirstName{get;set;}
public LastName{get;set;}
public bool IsValidUserDetails()
{
if(LastName == "Smith")
{
//We do not allow smiths, remember what happened last time ... or whatever
return false;
}
return true;
}
}
Hopefully this explains how responsibility is seperated. The form has no logic apart from display/formatting etc, it can also be stubbed out for testing . The presenter is the mediator between the view and the model and makes calls to services, the model implments your business logic. As already suggested there are variations on this pattern, which can make your code a bit slimmer and more flexible but this outlines the basic principles. I hope this helps.
:-)
With complex forms, I usually split the code into separate files. You can do this using "partial class". Each source code file is named based on the form. For example, MainForm.cs, MainForm.State.cs, MainForm.Update.cs, MainForm.Menu.cs and so on. If I have many complex forms, I will create a subfolder for each. The one tip here, is to create a MainForm.Wip.cs form. This partial class form is the code you are currently working on. Once you are done with this code, you can either rename it or move the code to other source code files.
In addition, I will also create user-controls. This has the benefit of code reuse and it moves a lot of the functionality out of the form. Check out "Developing Custom Windows Forms Controls with the .NET Framework" at http://msdn.microsoft.com/en-us/library/6hws6h2t.aspx.
Check out Nobody Cares What Your Code Looks Like at http://www.codinghorror.com/blog/2007/12/nobody-cares-what-your-code-looks-like.html. Something to think about before "organizing".
Have a look at the Model-View-Presenter patten: http://en.wikipedia.org/wiki/Model_View_Presenter
Using this pattern the code-behind of your forms should mainly contain simple cascading calls to the presenter, which in turn will alter the model, cascading events back to the view (sometimes through the presenter, depending on your implementation).
Point being: Your forms (the view) should not contain any state information; this will be in the presenter, and it should not care about where it gets it data from, as long as the data conforms to a specified contract. This promotes testability, as you can easily test your states and data on the presenter, and decouples the view allowing for PLAFs, different presentations of the same data and similar.
Good luck :)
Some quick suggestions:
Try to move all your non-UI code out of the forms, you only want to have GUI code in the actual form if possible. If a property has a side effect it should probably be a function. Your State property should almost certainly be a method and see if you can break out the code out of it into separate methods so it's just the one function call per state.
Here is a link to an architecture pattern used quite often.
http://en.wikipedia.org/wiki/Model_View_ViewModel
I would look up some other Architecture Patterns too, and research more into this one, look up some example code, etc.
I tend to put as much code as possible in usercontrols or custom controls. The components is easier to reuse, and the form-code is easier to read.
Usercontrols can also handle and expose events which can make the dynamic parts easier to seperate from the form code.
You can even make custom controls that can't be seen on the form like a Timer.
You should analyze your code first to divide what is application logic and what is UI logic. Both of those should never be in the same file. Your state propery is most definitely not UI logic, so move it out of your form first. This will help you clear out your form code.
Secondly, read up about some Design Patterns and principles. You can find some great examples here, in your case I would check out the behavioral patterns, more specifically the State and Mediator pattern. They're not silver bullets to fixing your problems, but it should give you a better idea of how to split your application and UI logic.
I use regions, like this:
#Region "_Edit"
Private Sub _edit_VisibleChanged(...) Handles _edit.VisibleChanged
End Sub
#End Region
from top to bottom, my forms code has:
- private declarations
- friend properties
- friend subs
- private properties
- private subs
- events
- event handlers for private forms/classes
it sounds like your State property needs to be broken up, or maybe have the code moved into other classes or routines so the complexity is more hidden.
精彩评论