Friday, May 23, 2008

WeI will continue the more practical part of this series by showing how the new mobile architecture looks in code, and if you want some background, please see the previous parts:

  • Part 1 was a general introduction
  • Part 2 talked about the changes in the lower tiers (logic + data, LINQ2SQL)
  • Part 3 discussed the changes in communication (WCF)
  • Part 4 covered important stuff in the user interface (MVC)
  • Part 5 summarized the theory and outlined the new architecture
  • Part 6 started the walkthrough of the architecture code by looking at the business domain
  • Part 7 continued the code walkthrough with a look at the service (WCF)
  • Part 8 covered the consumption of the service with .NET CF

The implemented architecture is published on CodePlex in a project called Windows Mobile Architecture Blueprint, and this means that you can access the full source code as well as discuss it, come with suggested improvements, etc. As I walk you through the creation of the architecture, we suggest you keep the source code handy to check out more details.

It's time to look at how we can implement the MVC pattern in the Windows Mobile client project that we created in the previous part of this series. As we've touched on before in this series, we have built this part of the architecture on a very simple and straightforward implementation by Alex Yakhnin, that he published as a small blog series last fall (see part 1 and part 2).

As Alex, I started with the core interfaces...

interface IView
{
   
void Show();
   
void Hide();
   
void Close();
   
string Text { get; set; }
}
interface IController
{
   
IView View { get; set; }
}

...and then the interface for the main view (form)...

interface IMainView : IView
{
   
Category SelectedCategory { get; }

   
void SetCategories(Category[] categories);
   
void SetProducts(string products);

   
event EventHandler Done;
   
event EventHandler CategorySelected;
}

...which already tells us what the main form can do. It allows us to set a number of categories (to choose from, SetCategories) and it will notify when a category is selected (CategorySelected). It will also make that selected category available through a public property (SelectedCategory) and allow us to set the products for a (the selected) category (SetProducts). Finally, it will notify when the user is done with it (Done).

The implementation of the controller for the main form looks like this...

class MainController : IController
{
   
private IMainView view;
   
private ServiceClient service;

   
public MainController(IMainView view)
    {
        View = view;

       
ServiceClient.EndpointAddress = new EndpointAddress("http://192.168.0.100:5610/Service.svc/basic");
        service =
new ServiceClient();

       
Category[] categories = service.GetCategories();
        view.SetCategories(categories);
    }

   
private void attachView(IMainView view)
    {
       
this.view = view;
        view.CategorySelected +=
new EventHandler(view_CategorySelected);
        view.Done +=
new EventHandler(view_Done);
    }

   
void view_CategorySelected(object sender, EventArgs e)
    {
        Category category = view.SelectedCategory;
       
string s = string.Empty;
       
foreach(Product product in category.Products)
            s += product.ProductName +
"\r\n";
        view.SetProducts(s);
    }

   
void view_Done(object sender, EventArgs e)
    {
        Application.Exit();
    }

    #region IController Members
   
public IView View
    {
       
get { return view; }
       
set { attachView(value as IMainView); }
    }
    #endregion
}

..and upon creation, the controller saves its view, and set up the event handlers to capture event from the view. Then it make the first call to the service to get the category list which is sent to the view. When a category is selected, a string is created (I know, in a real solution it would be a StringBuilder, but this code is simpler to read) and passed to the view.

The implementation of the main view (form) looks like this...

public partial class MainForm : Form, IMainView
{
    public MainForm()
    {
        InitializeComponent();
    }

    public Category SelectedCategory
    {
       
get { return categoryComboBox.SelectedItem as Category; }
    }

    public void SetCategories(Category[] categories)
    {
       
foreach(Category category in categories)
            categoryComboBox.Items.Add(category);
    }

   
public void SetProducts(string products)
    {
        productsTextBox.Text = products;
    }

    public event EventHandler CategorySelected;
   
private void categoryComboBox_SelectedIndexChanged(object sender, EventArgs e)
    {
       
if(CategorySelected != null)
            CategorySelected(
this, null);
    }

   
public event EventHandler Done;
   
private void doneMenuItem_Click(object sender, EventArgs e)
    {
       
if(Done != null)
            Done(
this, null);
    }
}

...and here we see that the categories are loaded in a combo box which is used to determine the currently selected category, and also to raise the event when a category is selected. The products string is simply put into a text box, and a menu option is used to offer the user to exit.

To manage (can cache) the various controllers and views, we use the following singleton class...

class ApplicationManager
{
   
public static readonly ApplicationManager Instance = new ApplicationManager();
   
private ApplicationManager() { }

   
private Dictionary<string, IController> controllersCache;

   
public MainForm GetMainForm()
    {
       
if(!controllersCache.ContainsKey("Main"))
            controllersCache.Add(
"Main", new MainController(new MainForm()));
       
return controllersCache["Main"].View as MainForm;
    }
}

...and the application is kicked off like this...

[MTAThread]
static void Main()
{
   
Application.Run(ApplicationManager.Instance.GetMainForm());
}

...which concludes the walkthrough of the basic architectore! In the next post, we will summarize this series of blog posts on the architecture blueprint implementation.