Demo para la formación del día 30/01/2018
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Fin/BaseForms
Img
Inicio/BaseForms
.gitignore
README.md

README.md

Xamarin Forms con MvvmCross 5.6

Demo para Xamarin Madrid

Tutorial de como crear una aplicación con Xamarin Forms y MvvmCross

En la carpeta inicio, se encuentra una solución con MvvmCross configurado, en la carpeta fin se encuentra el ejemplo completo del tutorial.

Paso 1 - Crear un proyecto Xamarin Forms

Abra visual studio y selecciona la opción crear nueva solución => Multiplataforma => Aplicación de Forms en blanco.

Paso 2 - Configurar MvvmCross

  1. Agregue .Core al nombre del proyecto PCL.
  2. Elimine la página que fue creada por los formularios de Xamarin.
  3. Agregue el paquete MvvmCross Forms StarterPack a los proyectos específicos de la plataforma y .Core
  4. En Android: elimine MainActivity.cs.
  5. En los proyectos específicos de la plataforma, elimine la referencia al proyecto principal y haga clic en Aceptar. Luego agrega la referencia nuevamente. (Esto tiene que hacerse porque los proyectos específicos de la plataforma no reconocerán el proyecto central renombrado)
  6. Cambie la acción de compilación de los siguientes archivos de 'Página' a 'Recurso integrado'(Embedded Resource): * Core|Pages|MainPage.xaml * Core|App.xaml

Seguiendo los pasos anteriores, MvvmCross ya estaría configurado. Al compilar se debe ver algo similar a la siguiente captura

Paso 3 - Archivo de arranque de la aplicación. CoreApp


 public override void Initialize()
        {
            CreatableTypes()
                .EndingWith("Service")
                .AsInterfaces()
                .RegisterAsLazySingleton();

            //No es necesario registrar las instancias que termina como Service, se registra automaticamente
            //Mvx.LazyConstructAndRegisterSingleton();

            //Configuramos la página de arranque, en nuestro caso será MainViewModel
            RegisterNavigationServiceAppStart();
        }


No es necesario aplicar ningún cambio. Si tenemos un servicio que el nombre no termine por Service, tenemos que registrar de la siguiente forma:

Mvx.LazyConstructAndRegisterSingleton();

Paso 4 - Crear un archivo de configuración

Creamos una carpeta con el nombre de Constants y dentro de ella, un CLASS estatica ConfigConstants.cs



using System;
namespace BaseForms.Constants
{
    public static class ConfigConstants
    {
        public const string keyApi = ""; // Código de la api themoviedb

        public const string baseUrl = "https://api.themoviedb.org/3/movie/upcoming?api_key="+keyApi+"&language=es-ES&page=1";

        public const string imgSmall = "http://image.tmdb.org/t/p/w185";

        public const string imgBig = "http://image.tmdb.org/t/p/w780";
    }
}


Paso 5 - Crear las interfaces de los servicios

En el proyecto de la PCL, vamos a crear una carpeta Interfaces y dentro de la misma 2 carpetas más Iconnectors y IWebServices

Dentro de la carpeta Iconnectors, crearemos la interfaz para acceder a HttpClient

IWebClientService.cs


using System;
using System.Net.Http;

namespace BaseForms.Interfaces.IConnectors
{
    public interface IWebClientService
    {
        HttpClient Client();   
    }
}


* Es ejemplo es muy básico, haga la interfaz según tu necesidad

La implementación de este archivo sería



using System;
using System.Net.Http;
using BaseForms.Interfaces.IConnectors;
using ModernHttpClient;

namespace BaseForms.Service.Connectors
{
    public class WebClientService : IDisposable, IWebClientService
    {
        private HttpClient client;

        public NativeCookieHandler CookieContainer { get; set; }

        public WebClientService()
        {
            this.client = this.GenerateHttpClient();
        }

        private HttpClient GenerateHttpClient()
        {
            this.CookieContainer = new NativeCookieHandler();

            NativeMessageHandler httpClientHandler = new NativeMessageHandler(
                false,
                true,
                this.CookieContainer
            );

            return new HttpClient(httpClientHandler);

        }

        public HttpClient Client()
        {
            return this.client;
        }


        public void Dispose()
        {
            this.client.Dispose();
        }
    }

}	



Para que funcione este código vamos a tener que instalar ModernHttpClient.

IMovieWebService

Agregaremos todos los servicios relacionados con las peliculas aqui


using System;
using System.Threading.Tasks;
using BaseForms.Models.Movie;

namespace BaseForms.Interfaces.IWebServices
{
    public interface IMovieWebService
    {
        Task Movie();
    }
}



	
using System;
using System.Globalization;
using System.Threading.Tasks;
using BaseForms.Constants;
using BaseForms.Interfaces.IConnectors;
using BaseForms.Interfaces.IWebServices;
using BaseForms.Models.Movie;
using Newtonsoft.Json;

namespace BaseForms.Service.WebServices
{
    public class MovieWebService : IMovieWebService
    {
        IWebClientService conection;

        public MovieWebService(IWebClientService conection)
        {
            this.conection = conection;
        }

        public async Task Movie()
        {
            var url = new Uri(string.Format(CultureInfo.InvariantCulture, ConfigConstants.baseUrl));
            var result = await conection.Client().GetAsync(url);
            if (result.IsSuccessStatusCode)
            {
                string content = await result.Content.ReadAsStringAsync();
                if (content != null)
                {
                    var response = JsonConvert.DeserializeObject(content);
                    response.IsCorrect = true;
                    return response;
                }
            }
            return new MovieResponse() { Mensage = "Servicio no disponible" };
        }
    }
}


Para que funcione y compile, tenemos que instalar newtonsoft y crear los modelos.

Paso 7 - Crear los modelos

Para manejar los datos devuelvo por el servicio, vamos a crear 3 Modelos.

  • BaseResponse.cs, para ayudar a tratar los errores
  • MovieResponse.cs, respuesta del servicio
  • ResultMovie.cs , es un objeto de MovieResponse, que va a devuelver el detalle por cada pelicula.

BaseResponse.cs

De forma predetermiada, inicializamos que la petición no ha sido bien realizada. Usaremos la propiedad IsCorrect para conocer si el resultado de la peticón ha sido código 200 o no.


namespace BaseForms.Models.Base
{
    public class BaseResponse
    {
        public string Mensage { get; set; }
        public bool IsCorrect { get; set; }
        public BaseResponse()
        {
            Mensage = "No se ha podido realizar la operación";
            IsCorrect = false;
        }
}

}

MovieResponse.cs

Respuesta del servicio, trataremos solo algunos campos usando la propiedad JsonProperty para cambiar el nombre de la propiedad.


using System;
using System.Collections.Generic;
using BaseForms.Models.Base;
using Newtonsoft.Json;

namespace BaseForms.Models.Movie { public class MovieResponse : BaseResponse {

    [JsonProperty("results")]
    public List<ResultMovie> Movies { get; set; }

    [JsonProperty("page")]
    public int Page { get; set; }

    [JsonProperty("total_results")]
    public int TotalResults { get; set; }

    //public Dates dates { get; set; }

    [JsonProperty("total_pages")]
    public int TotalPages { get; set; }

}

}

ResultMovie.cs

Modelo que contiene la información a pintar en la pantalla.


using System;
using BaseForms.Constants;
using BaseForms.Converters.Json;
using Newtonsoft.Json;

namespace BaseForms.Models.Movie { public class ResultMovie { [JsonProperty("vote_count")] public int VoteCount { get; set; }

    [JsonProperty("id")]
    public int Id { get; set; }

    [JsonConverter(typeof(BoolConverter))]
    [JsonProperty("video")]
    public bool Video { get; set; }

    [JsonProperty("vote_average")]
    public double VoteAverage { get; set; }

    [JsonProperty("title")]
    public string Title { get; set; }

    [JsonProperty("original_title")]
    public string OriginalTitle { get; set; }

    [JsonProperty("overview")]
    public string Overview { get; set; }

    [JsonProperty("backdrop_path")]
    public string Backdrop { get; set; }

    [JsonProperty("original_language")]
    public string OriginalLanguage { get; set; }

    public string ImgSmall => string.Format("{0}{1}", ConfigConstants.imgSmall, Backdrop);

    public string ImgBig => string.Format("{0}{1}", ConfigConstants.imgBig, Backdrop);

    /*
     * Propiedades sin tratar
    public double popularity { get; set; }
    public string poster_path { get; set; }
    public List<object> genre_ids { get; set; }
    public string backdrop_path { get; set; }
    public bool adult { get; set; }
    public string release_date { get; set; }
    */

}

}

Cabe destacar que se puede crear converters para tratar la información retornada por el servicios y que tambien se puede unir y crear nuevas propiedades igual a ImgSmall y ImgBig.

Paso 8 - Crear el converter JSON/newsoft


using System;
using Newtonsoft.Json;

namespace BaseForms.Converters.Json
{
    public class BoolConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteValue(((bool)value) ? 1 : 0);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return reader.Value.ToString() == "1";
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(bool);
        }
    }
}

Paso 9 - Crear el ViewModel Base

Todo lo que es generico en todos los viewmodels, lo meteremos aqui.En nuestro caso sería:

  • La navegación: navigationService
  • El web services de las peliculas: webMovieService
  • IsBusy , responsable por indicar si está esperando la respuesta del servicio o no.
  • Title , responsable por el título de la página

using System;
using BaseForms.Interfaces.IWebServices;
using MvvmCross.Core.Navigation;
using MvvmCross.Core.ViewModels;

namespace BaseForms.ViewModels
{
    public class BaseViewModel : MvxViewModel
    {
        protected readonly IMvxNavigationService navigationService;
        protected readonly IMovieWebService webMovieService;
        protected bool isBusy;
        protected string title;
        public BaseViewModel(IMvxNavigationService navigationService,IMovieWebService webMovieService)
        {
            this.navigationService = navigationService;
            this.webMovieService = webMovieService;
        }

        public bool IsBusy
        {
            get
            {
                return this.isBusy;
            }
            set
            {
                this.isBusy = value;
                this.RaisePropertyChanged(() => this.IsBusy);
            }
        }

        public string Title
        {
            get
            {
                return this.title;
            }
            set
            {
                this.title = value;
                this.RaisePropertyChanged(() => this.Title);
            }
        }



        public override void Prepare(object parameter)
        {

        }
    }
}




Paso 10 - Crear el ViewModel MainViewModel

Este viewmodel va a contener:

  • Herenda de BaseViewModel
  • Movies , listado de peliculas
  • ResultMovie, pelicula seleccionada
  • ViewAppeared, relacionado con el ciclo de vida de la aplicación. Se entra en está función cuando la vista se carga.
  • OpenModal , navega a otra página con los valores de la pelicula seleccionada

using System.Collections.Generic;
using System.Threading.Tasks;
using BaseForms.Interfaces.IWebServices;
using BaseForms.Models.Movie;
using BaseForms.ViewModels;
using MvvmCross.Core.Navigation;
using MvvmCross.Core.ViewModels;

namespace BaseForms.Core.ViewModels
{
    public class MainViewModel : BaseViewModel
    {
      
        public MainViewModel(IMvxNavigationService navigationService, IMovieWebService webMovieService) : base(navigationService, webMovieService)
        {
            
        }

        public override Task Initialize()
        {
            return base.Initialize();
        }

        public async override void ViewAppeared()
        {

            this.IsBusy = true;
            var response = await this.webMovieService.Movie();
            this.IsBusy = false;
            if (response.IsCorrect)
            {
                Movies = response.Movies;
            }
            else
            {
                //mostrar el mensaje de error
            }

        }

        private List movies;
        public List Movies
        {
            get
            {
                return this.movies;
            }

            set
            {
                this.movies = value;
                this.RaisePropertyChanged();

            }
        }
        private ResultMovie selectedMovie;
        public ResultMovie SelectedMovie
        {
            get
            {
                return this.selectedMovie;
            }

            set
            {
                this.selectedMovie = value;
                this.RaisePropertyChanged();
                this.OpenModal(value);
            }
        }

        private void OpenModal(ResultMovie value)
        {
            object parameter = value;
            navigationService.Navigate(parameter);

        }
    }
}


Paso 11 - Vista MainView

Preparando la vista, elementos a tener en cuenta:

  • MvxContentPage, fijate que estamos usando MvxContentPage en lugar de ContentPage
  • Que estamos relacionando el viewmodel con la vista. d:MvxContentPage x:TypeArguments="viewModels:MainViewModel
  • Que estamos cargando el template xmlns:templates="clr-namespace:BaseForms.Views.Template;assembly=BaseForms"
  • Que los valores que se pinta, viene de la propiedad Movies
  • SelectedMovie se usa para guardar el elemento seleccionado.

Código aqui Vista MainView




	
       

            
                    
                        
                            
                        
                    
                
            
        
	


Paso 12 - Crear template

Creamos una carpeta Views en el proyecto BaseForms.Core y luego otro carpeta más con el nombre de template dentro de la carpeta Views.

El próximo paso es crear nuestros templates distinto, para esto debemos crear una página contentView

Template 1 con imagen

Código aqui Template 1


	
	

	
       
        
         
            
                
                  
             
         

            
	



Template 2 sin imagen

Código aqui Template 2


	


	
        
        
            
        
            
         
	



Los templates son buenas formas de reutilizar código, para modificar el aspecto de la lista lo único que debemos indicar ahora es el nombre del template


 


Paso 13 - Crear el ViewModel AboutViewModel

Este viewmodel contiene:

  • CloseCommand => Un Command para cerrar la modal
  • La propiedad SelectedMovie, para pintar los datos en la pantalla
  • Prepare , este metodo, recupera el valor pasando por el MainViewModel.

using System;
using BaseForms.Interfaces.IWebServices;
using BaseForms.Models.Movie;
using BaseForms.ViewModels;
using MvvmCross.Core.Navigation;
using MvvmCross.Core.ViewModels;

namespace BaseForms.Core.ViewModels
{

    public class AboutViewModel : BaseViewModel
    {
       

        private IMvxCommand closeCommand;
        public IMvxCommand CloseCommand => closeCommand = closeCommand ?? new MvxCommand(DoCloseHandler);

        private ResultMovie selectedMovie;

        public AboutViewModel(IMvxNavigationService navigationService, IMovieWebService webMovieService) : base(navigationService, webMovieService)
        {
        }

        public ResultMovie SelectedMovie
        {
            get
            {
                return this.selectedMovie;
            }

            set
            {
                this.selectedMovie = value;
                this.RaisePropertyChanged();
            }
        }

        private void DoCloseHandler()
        {
            Close(this);
        }

        public override void Prepare(object parameter)
        {
            this.SelectedMovie = (ResultMovie)Convert.ChangeType(parameter, typeof(ResultMovie));
            this.Title = this.SelectedMovie.Title;
        }
    }
}


Paso 14 - Crear la AboutView

Código aqui Vista AboutView





      
         
    
    
        
            
                       
            
            
        
     



Para que se visualize como modal, debemos agregar el tipo de presentación en la vista. (MvxModalPresentation)


using System;
using System.Collections.Generic;
using BaseForms.Core.ViewModels;
using MvvmCross.Forms.Views;
using MvvmCross.Forms.Views.Attributes;
using Xamarin.Forms;

namespace BaseForms.Core.Pages
{

    [MvxModalPresentation(WrapInNavigationPage = true, Title = "Modal")]
    public partial class AboutPage : MvxContentPage
    {
        public AboutPage()
        {
            InitializeComponent();
        }


    }
}


El resultado final de este turtorial, se puede ver en la carpeta "FIN" de este repositorio.