Hands-On Mobile Development with .NET Core
上QQ阅读APP看书,第一时间看更新

Model-View-Controller (MVC) implementation

Both iOS and Android platforms are inherently designed to be used with a derivative of the Model-View-Controller (MVC) pattern. If we were dealing with a native application, it would have been the most logical path to use MVC for iOS and Model-View-Presenter (MVP) or a slightly derived version, the Model-View-Adapter (MVA), pattern, for Android:

The MVC pattern was born as a reaction to the single responsibility principle. In this pattern, the View (UI implementation) component is responsible for presenting the data that's received from the Model (service layer) and delegating the user input to the Controller so that the data changes can be propagated to the Model.

While it is being used widely with web applications, generally, a derived version is used for mobile and desktop applications. Derivatives of this pattern include MVA and MVP. 

In an MVA architecture (or Mediating Controller), the Adapter acts as a mediator between the View and Model, and is responsible for defining the strategy for one or more view components, as well as acting as the observer for these UI components:

In an MVC implementation, while using both classic and mediator patterns, the Controller becomes the heart and soul of the application. It needs to be aware of the Model, as well as the View (which is tightly coupled), since it implements a strategy for the view events (user input) that are acting as both the strategy implementer and observer.

Let's demonstrate this pattern while implementing a login view for our application:

  1. First, we need to create the view. Create a content page with a XAML design component:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="FirstXamarinFormsApplication.LoginView">
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="signUpButton" Text="Sign Up" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout VerticalOptions="CenterAndExpand"
Padding="20">
<Label Text="Username" />
<Entry x:Name="usernameEntry" Placeholder="username"/>
<Label Text="Password" />
<Entry x:Name="passwordEntry" IsPassword="true"
Placeholder="password" />
<Button x:Name="loginButton" Text="Login" />
<Label x:Name="messageLabel" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

  1. Next, create the two entries (that is, username and password), two buttons (that is, for login and signup), and finally a label field, which we will use to display the result of the login function.

  2. In order to support the implementation of a controller for this view that will handle the field validations, as well as the login and signup actions, expose the entry and button components to the associated controller:

 public partial class LoginView : ContentPage
{
// private LoginViewController _controller;

public LoginView()
{
InitializeComponent();

// _controller = new LoginViewController(this);
}

internal Entry UserName { get { return this.usernameEntry; }}

internal Entry Password { get { return this.passwordEntry; }}

internal Label Result { get { return this.messageLabel; }}

internal Button Login { get { return this.loginButton; }}

internal ToolbarItem SignUp { get { return this.signUpButton; }}
}
  1. Now, create the controller that will define the strategies for various events that we will need to handle:
 public class LoginViewController
{
private LoginView _loginView;

public LoginViewController(LoginView view)
{
_loginView = view;

_loginView.Login.Clicked += Login_Clicked;

_loginView.SignUp.Clicked += SignUp_Clicked;

_loginView.UserName.TextChanged += UserName_TextChanged;
}

void Login_Clicked(object sender, EventArgs e)
{
// TODO: Login
_loginView.Result.Text = "Successfully Logged In!";
}

void SignUp_Clicked(object sender, EventArgs e)
{
// TODO: Navigate to SignUp
}

void UserName_TextChanged(object sender,
TextChangedEventArgs e)
{
// TODO: Validate
}
}

As you can see,  even though we can further refactor our code so that we can insert abstraction layer(s) between the controller and the view, there is a strong coupling between the two. In order to remedy this bound, we can resort to a Model–View–ViewModel (MVVM) setup.