Este artigo foi traduzido por máquina.

Windows Phone

Compile aplicativos MVVM com Xamarin e MvvmCross

Thomas LeBrun

O padrão Model-View-ViewModel (MVVM) significa tornar-se o padrão de referência de escolha para qualquer aplicação de XAML (Windows Presentation Foundation [WPF], Windows 8, Windows Phone e Silverlight). Introduzido no início do WPF, separa as preocupações, testabilidade e muito mais. A melhor parte é que você pode usá-lo para qualquer outras tecnologias, mesmo aqueles que não usam XAML. Na verdade, você pode usar o padrão com ASP.NET, com JavaScript e muito mais.

Novell permite que você desenvolver aplicativos Android ou iOS no código c#. Esses aplicativos vêm com seus próprios modelos de desenvolvimento, mas graças a um quadro chamado MvvmCross, você pode trazer o padrão MVVM para estas plataformas também. Neste artigo, vou te dar tudo que você precisa entender o MvvmCross e como usá-la em seus aplicativos do Android e iOS.

Uma rápida olhada em MVVM

Tem havido muitos artigos cobrindo MVVM ultimamente, então não vou passar muito tempo analisando o padrão MVVM. Para resumir, MVVM é composto de três partes: o modelo (que corresponde aos dados que você vai querer exibir e manipular na tela), o modo de exibição (que é o componente de apresentação e a interface do usuário) e o ViewModel (que terá o modelo e exibi-lo no modo de exibição usando vinculação de dados e irá responder à interação do usuário). Figura 1 mostra uma representação gráfica do MVVM.

Overview of the Model-View-ViewModel Pattern
Figura 1 Visão geral do padrão Model-View-ViewModel

Ao desenvolver com tecnologias da Microsoft, é fácil ver a reusabilidade fornecida por MVVM. Mas e sobre tecnologias não-Microsoft? E o Android? E o iOS?

Claro, você ainda pode implementar seus próprios padrões ou metodologias, mas aqueles não podem fornecer alguns dos recursos mais poderosos do MVVM, tais como a vinculação de dados e capacidade de teste. Um dos maiores benefícios do MVVM seguinte é os ViewModels são facilmente testável. Isso também permite que você insira código multi-plataforma ViewModel. Esse código poderia ser contido em uma plataforma -­classe específica, como um controlador.

Novell e MvvmCross "resolver" isso e oferecem uma forma unificada de uso MVVM em outras plataformas. Antes de examinar utilizando MVVM em outras plataformas, eu vou levar um momento para explicar a Novell.

Novell para aplicações Android/iOS

Novell é um conjunto de ferramentas que proporciona alta performance de código compilado com acesso total a todas as APIs nativas. Permite que você crie aplicativos nativos com experiências específicas de dispositivo. Qualquer coisa que você pode fazer em Objective-C ou Java, você pode fazer em c# com Novell.

Enquanto você pode usar o Novell Studio para desenvolver aplicativos, você também pode usar Visual Studio e todas as outras ferramentas que você já usa para c# desenvolvimento hoje. Isso inclui Team Foundation Server (para o controle de origem) e plug-ins como o Resharper, GhostDoc e assim por diante.

Na perspectiva de um desenvolvedor, Novell oferece três principais products—Xamarin.Mac, Xamarin.iOS (MonoTouch.dll) e Xamarin.Android (Mono.Android.dll). Todos estes são desenvolvidos em cima de Mono, a versão open source do Microsoft .NET Framework. Mono foi na verdade inicialmente criado por Miguel De Icaza, o co-fundador e atual CTO da Novell.

No iOS, um dedicado compilador compila aplicativos escritos em c# diretamente para código nativo do braço. Para o Android, o processo é semelhante à execução e compilação do .NET. O código-fonte é compilado para uma linguagem intermediária (IL). Quando o código é executado no dispositivo, uma segunda compilação (realizada na hora certa) compila o código IL para código nativo. Isso faz sentido porque os aplicativos Android são desenvolvidos em Java, que possui uma arquitetura interna similar à arquitetura do .NET Framework. Você pode ver uma representação visual do processo de compilação para iOS e Android em Figura 2.

A compilação nativa com Novell
Figura 2 a compilação nativa com Novell

Na maioria das vezes, você não precisa se preocupar com o gerenciamento de memória, alocação de recursos e assim por diante, porque tudo é gerenciado pelo tempo de execução que fornece Novell. No entanto, há casos em que você tem que estar ciente do que está acontecendo, como interoperabilidade com Objective-C. Isso poderia criar manter ciclos ou em sua classe gerenciado na verdade quebra alguns recursos caros, como UIImage no iOS. Para mais informações sobre isto, consulte bit.ly/1iRCIa2.

Existem considerações especiais para aplicativos de iOS. Enquanto Novell Studio num Mac oferece tudo o necessário para iOS devel­desenvolvimento, usuários de Visual Studio em um PC ainda terá um Mac com Novell ferramentas instaladas. Isso permite que você compilar aplicativos na rede e testá-los sobre o iOS Simulator ou um dispositivo iOS.

Novell permite construir iOS e Android aplicativos usando c# ou F #, mas usando o padrão Model-View-Controller tradicional. Se você quer portabilidade, facilidade de manutenção e aumentada de capacidade de teste, você precisa de uma maneira de trazer o padrão MVVM para essas plataformas. Digite MvvmCross.

MvvmCross para Novell Apps

MvvmCross é uma fonte aberta, quadro MVVM multi-plataforma desenvolvido por Stuart Lodge. Está disponível para Windows Phone, Windows 8, iOS, Android e WPF aplicativos. MvvmCross traz o padrão MVVM para plataformas onde foi anteriormente indisponível, como iOS e Android.

Ele também suporta vinculação de dados em modos de exibição. Este é um recurso poderoso que fornece grande separação de preocupações. A vista usará os ViewModels para oferecer comportamentos adequados no aplicativo. MvvmCross mesmo localiza os ViewModels em um projeto dedicado, assim você pode facilmente fazer referência e reutilizá-los em outros.

Este é o ponto mais importante quando se fala em MvvmCross. Localizando os ViewModels em uma biblioteca de classe portátil (PCL), você pode adicioná-los como referência para quaisquer outros projetos. Claro, isso não é o ponto de MvvmCross apenas interessante. Há também uma arquitetura plug-in, injeção de dependência (DI) e muito mais.

Usando MvvmCross no Android/iOS

Usar MvvmCross é fácil, porque é apenas alguns pacotes de NuGet que você adicionar aos seus projetos. Depois de feito isso, existem alguns passos menores, que você tem que tomar antes de lançar o aplicativo. As etapas variam um pouco entre iOS e Android, mas eles são bastante semelhantes. O projeto do núcleo contém seus ViewModels e a classe App. Isso inicializa os serviços e define o ViewModel que terá início no lançamento:

public class App : MvxApplication
{
  public override void Initialize()
  {
    this.CreatableTypes()
      .EndingWith("Service")
      .AsInterfaces()
      .RegisterAsLazySingleton();
    this.RegisterAppStart<HomeViewModel>();
  }
}

Em seu iOS ou Android aplicativo, você precisa criar um arquivo de Setup.cs. Este será o projeto núcleo de referência e avise-o tempo de execução como instanciar o aplicativo:

public class Setup : MvxAndroidSetup
{
  public Setup(Context applicationContext) : base(applicationContext)
  {
  }
  protected override IMvxApplication CreateApp()
  {
    return new Core.App();
  }
}

Então o arquivo de instalação cria o aplicativo usando o arquivo app. O último indica que o tempo de execução para carregar um determinado ViewModel na inicialização usando o método RegisterAppStart.

Cada modo de exibição é específico para cada aplicação. Esta é a única parte que muda. No Android, a classe herda de MvxActivity (atividades padrão no Android herdam de atividade). Para iOS, os pontos de vista herdam de MvxViewController (padrão ViewController na iOS herdam UIViewController):

[Activity(ScreenOrientation = ScreenOrientation.Portrait)]
public class HomeView : MvxActivity
{
  protected override void OnViewModelSet()
  {
    SetContentView(Resource.Layout.HomeView);
  }
}

MvvmCross precisa saber o ViewModel que está associado um modo de exibição. Você pode fazer isso por padrão graças a Convenção de nomenclatura. Você pode também facilmente alterar isso substituindo a propriedade ViewModel da vista ou usando o MvxViewForAttribute:

[Activity(ScreenOrientation = ScreenOrientation.Portrait)]
[MvxViewFor(typeof(HomeViewModel))]
public class HomeView : MvxActivity
{ ... }

No iOS e Android, as vistas são projetadas com diferentes abordagens. No iOS, o modo de exibição é definido no código c#. No Android, você também pode usar o código c#. Melhor ainda, no entanto, você pode usar o formato de mapeamento (formato XML usado para descrever a interface do usuário no Android). Devido a essas diferenças, as ligações de dados são definidas de forma diferente em cada plataforma.

No iOS, você pode criar um BindingDescriptionSet para representar o elo entre a View e ViewModel. Esse conjunto, você pode especificar qual controle que você deseja vincular a qual propriedade antes de aplicar a vinculação:

var label = new UILabel(new RectangleF(10, 10, 300, 40));
Add(label);
var textField = new UITextField(new RectangleF(10, 50, 300, 40));
Add(textField);
var set = this.CreateBindingSet<HomeView, 
  Core.ViewModels.HomeViewModel>();
set.Bind(label).To(vm => vm.Hello);
set.Bind(textField).To(vm => vm.Hello);
set.Apply();

No Android usando o mapeamento, você pode usar o novo atributo XML MvxBind para executar a vinculação de dados:

<TextView xmlns:local="http://schemas.android.com/apk/res-auto"
          android:text="Text"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:id="@+id/tripitem_title"
          local:MvxBind="Text Name"
          android:gravity="center_vertical"
          android:textSize="17dp" />

O atributo MvxBind leva-se em parâmetros que especificam a propriedade do controle para ligar e a propriedade de ViewModel para usar como a fonte. Se você for um desenvolvedor XAML, esteja ciente de que o modo de ligação MvvmCross é TwoWay por padrão. Em XAML, o modo de ligação padrão é OneWay. O quadro de MvvmCross compreende alguns atributos XML personalizados disponíveis em Mvx­BindingAttributes.xml, como você pode ver na Figura 3.

Figura 3 conteúdo do arquivo MvxBindingAttributes.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-stylable name="MvxBinding">
    <attr name="MvxBind" format="string"/>
    <attr name="MvxLang” format="string"/>
  </declare-styleable>
  <declare-stylable name="MvxControl">
    <attr name="MvxTemplate" format="string"/>
  </declare-styleable>
  <declare-styleable name="MvxListView">
    <attr name="MvxItemTemplate" format= "string"/>
    <attr name="MvxDropDownItemTemplate" format="string"/>
  </declare-stylable>
  <item type="id" name="MvxBindingTagUnique">
  <declare-styleable name="MvxImageView">
    <attr name="MvxSource" format="string"/>
  </declare-stylable>
</resources>

O conteúdo desse arquivo é simples, mas muito importante. O arquivo indica os atributos que você pode usar os arquivos de mapeamento. Assim, vê-se uma operação de vinculação, você pode usar atributos de MvxBind ou MvxLang. Você também pode usar alguns novos controles (MvxImageView, MvxListView). Cada um deles tem dedicado atributos personalizados, como você pode ver na Figura 4.

Figura 4 novos controles possuem atributos personalizados

<LinearLayout
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:layout_weight="2">
  <Mvx.MvxListView
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    local:MvxBind="ItemsSource Trips;ItemClick SelectTripCommand"
    local:MvxItemTemplate="@layout/tripitemtemplate" />
</LinearLayout>

Isto deve ser familiar para os desenvolvedores do XAML. Os ItemsSource e ItemClick propriedades são vinculados a algumas propriedades de fonte de dados (o ViewModel neste caso). O MvxItemTemplate define a interface para cada item no ListView.

Você poderia esperar a sintaxe para iOS bem diferente, mas, na realidade, é na verdade bastante semelhante. A sintaxe usada no arquivo de mapeamento, referida como "Fluente" vinculação, é simplesmente uma vinculação de formato de texto, que você pode mapear para o c# versão também. O comando usado no exemplo anterior para selecionar um item na lista é um objeto ICommand:

public ICommand<Trip> SelectTripCommand { get; set; }

A implementação desta interface é fornecida por MvvmCross usando a classe MvxCommand:

private void InitializeCommands()
{
  this.SelectTripCommand = new MvxCommand<Trip>(
    trip => this.ShowViewModel<TripDetailsViewModel>(trip),
    trip => this.Trips != null && this.Trips.Any() && trip != null);
}

Um problema comum quando se utiliza o padrão MVVM é conversão de tipos. Isso acontece quando você definir as propriedades usando um tipo que não é consumível diretamente pela interface do usuário. Por exemplo, você pode ter uma propriedade de imagem como uma matriz de bytes, mas você quer usá-lo para a propriedade da fonte de um controle de imagem. Em XAML, você pode resolver este problema com a interface IValueConverter, que mapeia os valores entre a View e ViewModel.

O processo é bastante semelhante com MvvmCross, graças a interface IMvxValueConverter e seus dois métodos, Convert e ConvertBack. Essa interface permite que você execute conversões similares ao XAML, mas com um objetivo de cruz-tecnologia em mente. Tenha em mente que esta interface tem a mesma desvantagem como XAML. Ele utiliza um objeto como um parâmetro e retorna como o valor. Então a carcaça é necessária. Para otimizar este, MvvmCross oferece a classe genérica de MvxValueConverter, que leva em parâmetros de entrada e os tipos de retorno:

public class ByteArrayToImageConverter : 
  MvxValueConverter<byte[], Bitmap>
{
  protected override Bitmap Convert(byte[] value, Type targetType,
    object parameter, CultureInfo culture)
  {
    if (value == null)
        return null;
    var options = new BitmapFactory.Options { InPurgeable = true };
    return BitmapFactory.DecodeByteArray(value, 
      0, value.Length, options);
  }
}

Referenciar o conversor é fácil. No iOS, use o método WithConversion na sintaxe fluente:

var set = this.CreateBindingSet<HomeView, 
  Core.ViewModels.HomeViewModel>();
set.Bind(label).To(vm => vm.Trips).WithConversion("ByteArrayToImage");
set.Apply();

Em Android, o conversor diretamente no arquivo de mapeamento de referência:

<ImageView
  local:MvxBind="Bitmap Image,Converter=ByteArrayToImage"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content" />

Conversores estão localizados por seus nomes usando reflexão. Por padrão, o framework irá procurar qualquer tipo contendo "conversor" no nome. Você pode também manualmente registrar conversores, substituindo o método FillValueConverters na classe de configuração.

MvvmCross fornece um DIcontainer simples e leve. Você pode registrar as classes e interfaces no recipiente com vários padrões, incluindo um registro de singleton, um registro dinâmico e muito mais:

Mvx.RegisterType<ISQLiteConnectionFactory, SQLiteConnectionFactory>();
Mvx.RegisterSingletong<ISQLiteConnectionFactory, SQLiteConnectionFactory>();

Resolução de tipos no recipiente pode acontecer de duas maneiras. Primeiro, você pode usar o método Mvx.Resolve para resolver explicitamente o tipo. Ele também suporta injeção de construtor, que permite MvvmCross executar a reflexão e resolver automaticamente parâmetros durante a criação do objeto:

private readonly ISQLiteConnectionFactory _sqlFactory;
public DataAccessLayerService(ISQLiteConnectionFactory sqlFactory)
{
  this._sqlFactory = sqlFactory;
}

Você pode usar injeção de construtor para serviços, bem como ViewModels. É importante compreender porque qualquer serviços desenvolvidos para sua aplicação colocar no projeto núcleo são, por padrão, cruz-plataforma.

Você também pode aproveitar os serviços que parecem ser multi-plataforma, mas para qual implementação é específico da plataforma. Por exemplo, tirar uma foto com a câmera, ficando o usuário coordena, usando um banco de dados e assim por diante. Com injeção de construtor, ViewModels pode receber uma interface onde a implementação é específico da plataforma.

Para definir ainda mais este mecanismo de injeção e código específico da plataforma, o MvvmCross fornece um sistema plug-in. O sistema permite criar e injetar novos recursos em tempo de execução. Cada plug-in é um serviço exposto uma interface, que tem uma implementação concreta fornecida para cada plataforma. O sistema plug-in registra a interface e implementação. Aplicativos consomem os plug-ins com DI. DI também permite que desenvolvedores de plug-in oferece uma implementação simplificada "zombar" durante o desenvolvimento e testes.

Do ponto de vista de um desenvolvedor, escrever um plug-in é tão simples como escrever uma interface. Crie uma classe que implementa essa interface e um carregador de plug-in. O gestor de plug-in é uma classe que implementa a interface IMvxPluginLoader. Ele registra o plug-in interface e implementação (usando Mvx.RegisterType) quando seu método EnsureLoaded é chamado.

Existem muitos plug-ins já disponível. Fornecerão recursos como acesso a arquivos, e-mail, conversão JSON e assim por diante. Você pode encontrar a maioria deles usando NuGet. Esteja ciente de que alguns plug-ins não inclui implementações para todas as plataformas. Preste atenção para este detalhe quando tenciona utilizar um plug-in. Mesmo se um plug-in está faltando apoio para sua plataforma, você pode encontrá-lo mais fácil de seguir o padrão e implementar a plataforma falta em vez de criar um novo plug-in por conta própria. Se isto acontecer, considere contribuir sua implementação para o proprietário do plug-in para que outros também podem usá-lo também.

MvvmCross é um quadro valioso. Considere isso quando estiver desenvolvendo aplicativos móveis — mesmo em iOS e Android. O padrão MVVM, juntamente com a vinculação de dados e plug-ins, fornece um sistema poderoso para criar código altamente sustentável e portátil.


Thomas Lebrun é um consultor na Praça do infinito, um parceiro Microsoft francês trabalhando em tecnologias como Windows 8, Windows Phone, Windows Presentation Foundation (WPF), Silverlight, superfície e muito mais. Ele escreveu dois livros sobre WPF e o padrão MVVM. Ele também é palestrante regular em eventos da Comunidade. Você pode segui-lo é blog em blog.thomaslebrun.net e no Twitter em twitter.com/thomas_lebrun.

Agradecemos ao seguinte especialista técnico da Microsoft pela revisão deste artigo: Jared Bienz