(1) Model ---Reference Data
Data Entry windows almost always need reference data to guide user input. A dropdown list filled with 50 US States is a perfect example. Naturally, we would want a simple type structure class and a complex type storage class to hold 50 states for databinding purposes:
public struct State
{
public State(int? id, string name)
{
_StateID = id;
_StateName = name;
}
private int? _StateID;
public int? StateID
{
get { return _StateID;}
set { _StateID = value; }
}
private string _StateName;
public string StateName
{
get { return _StateName; }
set { _StateName = value; }
}
}
public class States
{
private static List
static States()
{
_StateList.Add(new State(0, "MA"));
....
_StateList.Add(new State(null, "(empty)")); // add (empty) for user to de-select to
}
public static List
{
get { return _StateList; }
}
}
(2) Model --- Entity Data
Suppose UI is designed to present User an address for View/Edit/Create, we then need an entity to represent Address:
public class Address
{
private int? _StateID;
public int? StateID
{
get { return _StateID; }
set { _StateID = value; }
}
}
Note Database normalization requires only saving StateID to database and avoid duplicating StateName.
(3) Presenter --- one way databinding to View
Upon View calling its single entry point, a Presenter will initialize View, Load Data from Model and Execute Business logic (e.g. show, update, add):
public interface IAddressView
{
void ShowReferenceData();
void ShowCurrentAddress();
}
class AddressPresenter
{
private IAddressView _view;
public AddressPresenter(IAddressView view)
{
_view = view;
}
public void Render(int? UserID)
{
LoadEntityData(UserID);
_view.ShowReferenceData();
_view.ShowCurrentAddress();
}
private ASC.ReferenceData.State _State;
public ASC.ReferenceData.State State
{
get { return _State;}
set { _State=value;}
}
private ASC.Entity.Address _Address;
private void LoadEntityData(int? UserID)
{
// simulate retrival of Address from Database
_Address = new ASC.Entity.Address();
if (UserID >0) _Address.StateID = 3;
// All UserID <=0 will have empty selection of state in its Address Entity;
foreach (ASC.ReferenceData.State s in ASC.ReferenceData.States.StateList)
{
if (s.StateID == _Address.StateID)
{
_State = new ASC.ReferenceData.State(_Address.StateID, s.StateName);
break;
}
}
}
public void Update()
{
// Save Entity to Database
}
public void Add()
{
// Insert Entity to Database
}
}
Note that two properties in Address Entity Data are represented as a single property of type ASC.ReferenceData.State. This will enable loading into a ComboBox for ease of databinding.
(4) View --- Implement IView and Reverse DataBind
As before, View need to implement IView with consideration of reference data
public partial class AddressChangeForm : Form, IAddressView
{
public void ShowReferenceData()
{
this.cbState.DataSource = ASC.ReferenceData.States.StateList;
this.cbState.DisplayMember = "StateName";
}
public void ShowCurrentAddress()
{
cbState.DataBindings.Add("SelectedItem", _Presenter, "State",false,DataSourceUpdateMode.OnPropertyChanged, new ASC.ReferenceData.State(null,"(empty)"));
}
Note that Reference data are push to View using one-way databinding, while Presenter data are push to View with change probagated backwards through OnPropertyChanged.
Presenter entry point will be called upon user action:
private void btnGetCurrent_Click(object sender, EventArgs e)
{
cbState.DataBindings.Clear();
_Presenter = new AddressPresenter(this);
_Presenter.Render(1078);
}
And a new presenter need to be created upon User Add New action:
private void btnAdd_Click(object sender, EventArgs e)
{
.....
cbState.DataBindings.Clear();
_Presenter = new AddressPresenter(this);
_Presenter.Render(-1);
......
_Presenter.Add();
cbState.DataBindings.Clear();
.....
}