Usar imagenes de byte[] en Xamarin Forms con XAML

Me surgio esta necesidad porque tengo varias aplicaciones que reciben imagenes desde el servidor. La

Me surgio esta necesidad porque tengo varias aplicaciones que reciben imagenes desde el servidor.

La solución viene fundamentalmente de estas dos fuentes:

Como usar imagenes en XAML que esten en el proyecto shared

Convertir de byte[] a Image en Xamarin

La primera parte es crear una Extension de Xaml que permita vincular strings al Source del objeto Image, para ellos creamos una clase así:

    using System;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;

    [ContentProperty("Source")]
    public class ImageResourceExtension : IMarkupExtension
    {
        public string Source { get; set; }
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            if (Source == null)
                return null;
            var imageSource = ImageSource.FromResource(Source);
            return imageSource;
        }
    }

Lo siguiente es crear una clase conversor que transforme una fuente byte[] en un ImageSource:

    using System;
    using System.Globalization;
    using System.IO;
    using Xamarin.Forms;
    class ByteArrayToImageSourceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            ImageSource retSource = null;

            if (value != null)
            {
                byte[] imageAsBytes = (byte[])value;
                retSource = ImageSource.FromStream(() => new MemoryStream(imageAsBytes));
            }
            else
                retSource = ImageSource.FromResource("IMAGEN DEFAULT");

            return retSource;
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

En mi caso he puesto que si no recibe ningún valor llame a una imagen default.

En la página xaml donde queramos usarlo tenemos que añadir la referencia al conversor:

      <ContentPage.Resources>
        <ResourceDictionary>
          <conv:ByteArrayToImageSourceConverter x:Key="bic" />
        </ResourceDictionary>
      </ContentPage.Resources>

De esa manera ya podemos vincular la propiedad byte[] a la imagen asi:

                 <Image
                        Aspect="AspectFit"
                        HorizontalOptions="FillAndExpand"
                        VerticalOptions="FillAndExpand"
                        Source="{Binding imagen, Converter={StaticResource bic}}"/>

 

Consumir webservice asmx asp.net en Xamarin Forms

Suelo desarrollar servicios web que asp.net para clientes que usan windows server. En xamarin androi

Suelo desarrollar servicios web que asp.net para clientes que usan windows server. En xamarin android era muy fácil usarlos, bastaba con añadirlos como referencia web, pero en xamarin forms me parecia imposible hasta que encontre este post How to Consume WCF Service in Xamarin.forms PCL ?

A continuación describo los pasos:

Lo primero de todo es que para poder usar esta solución hay que cambiar los destinos del proyecto Pcl. Para poder hacerlo lo primero es modificar los paquetes nuget del proyecto compartido y quitar el de Xamarin.Forms.

Despues pinchamos en propiedades del proyecto Pcl, y en la primera pestaña cambiamos los destinos quitando Windows Phone 8.1. Aceptamos y volvemos a poner en Nuget el paquete de Xamarin.Forms.

Ahora viene la parte en la que añadimos la clase que maneja el webservice a nuestro proyecto, vamos a generar el fichero con una herramienta externa y lo añadiremos al proyecto.

Con la consola de simbolo de sistema de windows vamos a C:\Program Files (x86)\Microsoft SDKs\Silverlight\v5.0\Tools\

Ahi vamos a ejecutar un comando que se conectara a nuestro webservice y creara la clase, para un webservice alojado por ejemplo en http://miservidor.com/webservice/servicio.asmx vamos a ejecutar slsvcutil http://miservidor.com/webservice/servicio.asmx?WSDL /out:servicio.cs

Esto creara en la carpeta de la herramienta un fichero servicio.cs que tenemos que añadir a nuestro proyecto Pcl.

Una vez hecho para llamar a una función en el servicio web hay que hacerlo así:

                var binding = new BasicHttpBinding()
                {
                    MaxBufferSize = 2147483647,
                    MaxReceivedMessageSize = 2147483647
                };
                IsBusy = true;
                servicioSoapClient servicio = new servicioSoapClient(
                  binding,
                  new EndpointAddress("http://miservidor.com/webservice/servicio.asmx"));
                servicio.FuncionCompleted += OnGotResult;
                servicio.FuncionAsync(parametros);

Y crear un metodo que reciba los resultados:

        void OnGotResult(object sender, FuncionCompletedEventArgs e)
        {
            Device.BeginInvokeOnMainThread(() => {
                string error = null;
                if (e.Error != null)
                    error = e.Error.Message;
                else if (e.Cancelled)
                    error = "Cancelled";

                if (!string.IsNullOrEmpty(error))
                {
                    string err = error;
                }
                else
                {
                    //recibes e.Result y ya haces con ello lo que necesites
                }
            });
        }

Es un poco lioso la primera vez, pero funciona muy bien.

Navegación en Xamarin Forms con Xaml y Mvvm sin code-behind

Me gusta desarrollar en Xamarin Forms usando Xaml y el patr&amp;oacute;n Mvvm lo m&amp;aacute;s limpio posib

Me gusta desarrollar en Xamarin Forms usando Xaml y el patrón Mvvm lo más limpio posible.

Me surgio el problema de como hacer las navegaciones de pantallas sin codebehind y encontre la siguiente solución (no encuentro de donde la saque, cuando lo encuentre actualizare la fuente).

Creamos una clase NavigationService así:

    using System.Threading.Tasks;
    using ViewModel;
    using Xamarin.Forms;

    public static class NavigationService
    {
        public static async Task PushAsync(BaseViewModel viewModel)
        {
            var view = Registro.GetPage(viewModel.GetType());
            view.BindingContext = viewModel;
            await Navigation.PushAsync(view);
        }

        public static async Task PopAsync()
        {
            await Navigation.PopAsync();
        }

        public static async Task PushModalAsync(BaseViewModel viewModel, bool wrapInNavigation = true)
        {
            var view = Registro.GetPage(viewModel.GetType());
            view.BindingContext = viewModel;
            await Navigation.PushModalAsync(wrapInNavigation ? new NavigationPage(view) : view);
        }

        public static async Task PopModalAsync()
        {
            await Navigation.PopModalAsync();
        }

        public static void SetRoot(object viewModel, bool wrapInNavigation = true)
        {
            var view = Registro.GetPage(viewModel.GetType());
            view.BindingContext = viewModel;
            Application.Current.MainPage = wrapInNavigation ? new NavigationPage(view) : view;
        }

        static INavigation Navigation
        {
            get
            {
                //If the tab page has Navigation controllers as the contents, we need to use those.
                var tabbed = Application.Current.MainPage as TabbedPage;
                return tabbed?.CurrentPage?.Navigation ?? Application.Current.MainPage.Navigation;
            }
        }
    }

Ahora creamos una clase Register (yo la cree como Registro) así:

    using System;
    using System.Collections.Generic;
    using ViewModel;
    using Xamarin.Forms;
    public static class Registro
    {
        static readonly Dictionary<Type, Type> RegisteredTypes = new Dictionary<Type, Type>();

        static readonly Dictionary<Type, object> Singletons = new Dictionary<Type, object>();

        public static void Register<TType, TType1>() where TType1 : TType
        {
            RegisteredTypes[typeof(TType)] = typeof(TType1);
        }

        public static void Register(Type type, Type type2)
        {
            RegisteredTypes[type] = type2;
        }

        public static void RegisterPage<TType, TType1>() where TType1 : Page where TType : BaseViewModel
        {
            RegisteredTypes[typeof(TType)] = typeof(TType1);
        }

        public static Page GetPage(BaseViewModel model, bool singleton = true)
        {
            var page = GetPage(model.GetType(), singleton);
            page.BindingContext = model;
            return page;
        }

        public static T GetPage<T>(BaseViewModel model, bool singleton = true) where T : Page
        {
            var page = GetPage(model, singleton);
            return (T)page;
        }

        public static Page GetPage<T>(bool singleton = true)
        {
            return GetObject<T, Page>(singleton);
        }

        public static Page GetPage(Type type, bool singleton = true)
        {
            return GetObject<Page>(type, singleton);
        }

        public static T GetObject<T>(bool singleton = true)
        {
            return GetObject<T, T>(singleton);
        }
        public static T1 GetObject<T, T1>(bool singleton = true)
        {
            return GetObject<T1>(typeof(T), singleton);
        }
        public static T GetObject<T>(Type type, bool singleton = true)
        {
            Type objectType;
            if (!RegisteredTypes.TryGetValue(type, out objectType))
                return default(T);
            if (!singleton)
                return (T)Activator.CreateInstance(objectType);
            Object item;
            if (!Singletons.TryGetValue(objectType, out item))
            {
                Singletons[objectType] = item = (T)Activator.CreateInstance(objectType);
            }
            return (T)item;
        }
    }

Esta clase incluye un BaseViewModel que en mi caso es una clase ViewModel de la que heredan todas las demas, y donde yo pongo entre otras cosas la implementación de INotifyPropertyChanged, pero que no requiere ningún código especial para la navegación.

En App.xaml.cs hay que registrar cada view con su ViewModel así:

        void RegisterPages()
        {
            Registro.RegisterPage<LoginViewModel, Login>();
            Registro.RegisterPage<VisitasViewModel, MainPage>();
            Registro.RegisterPage<DetalleViewModel, DetalleDatos>();
        }

Y en el metodo public App() poner una llamada a ese metodo, una vez eso ya haces la llamada a la primera navegación. En el código que te pongo por ejemplo primero en otro sitio si el usuario esta registrado ya o no.

        public App()
        {
            InitializeComponent();
            RegisterPages();
            CheckUser();
            if (IsUserLoggedIn)
                NavigationService.SetRoot(new VisitasViewModel());
            else
                NavigationService.SetRoot(new LoginViewModel());
        }

En el resto de páginas navegas usando los metodos PopAsync, PushAsync o los Modal igual como aqui Hierarchical Navigation

Minitutorial Android: Mantener las variables al girar la pantalla o al recuperar el estado

Siguiendo con la calculadora a muchos nos ha surgido el problema de que, al cambiar de orientaci&amp;oac

Siguiendo con la calculadora a muchos nos ha surgido el problema de que, al cambiar de orientación la activity se "destruye" y "vuelve a crear",  perdiendose de esta manera las variables en uso. He encontrado una muy buena solución en StackOverflow y voy a explicarla y ampliarla un poquito:

La base del sistema es: creamos un objeto Bundle (el mismo que recibe el onCreate) y lo llenamos con las variables que deseamos que permanezcan, despues le decimos al onCreate y a un nuevo metodo llamado onRestoreInstanceState que deben hacer con esos datos recibidos.

Paso Uno : Guardar la información
Tenemos que crear el metodo onSaveInstanceState de una forma parecida a esta:

      @Override  
      public void onSaveInstanceState(Bundle savedInstanceState) {  
       super.onSaveInstanceState(savedInstanceState);  
       //Guarda datos en el objeto savedInstanceState que es el que recibe onCreate  
       savedInstanceState.putDouble("Resultado", Resultado);  
       savedInstanceState.putDouble("Memoria", Memoria);  
       savedInstanceState.putInt("Operacion", Operacion);  
       savedInstanceState.putString("LineaComando", LineaComando);  
       savedInstanceState.putString("Numero", Numero);  
      }  

Básicamente lo que hace es añadir a savedInstanceState variables de tipo determinado con un  nombre (no tiene que ser el mismo que el de la variable que pasamos, solo el mismo que usaremos en el retorno) asignandoles el valor de la variable que queremos guardar.

Paso Dos: Recuperar los datos en onCreate
Ahora tenemos que volcar esos datos de vuelta a sus variables en el onCreate, claro que si, como en mi caso, inicializamos las variables a 0 en el onCreate, debemos asegurarnos primero que el savedInstanceState tenga algo que darnos:

      @Override  
      protected void onCreate(Bundle savedInstanceState) {  
           super.onCreate(savedInstanceState);  
           setContentView(R.layout.activity_main); 
           //Comprobamos que savedInstanceState tiene datos
           if(savedInstanceState!=null)  
           {  
                 Resultado = savedInstanceState.getDouble("Resultado");  
                 Memoria = savedInstanceState.getDouble("Memoria");  
                 if(Memoria!=(double)0)  
                 {
                          //Aqui lo que hagc es volver visible el texto M
                          TextView M = (TextView)findViewById(R.id.Memoria);  
                          M.setVisibility(View.VISIBLE);  
                 }  
                 Operacion = savedInstanceState.getInt("Operacion");  
                 LineaComando = savedInstanceState.getString("LineaComando");  
                 if(LineaComando.length()>0)  
                 {  
                          //Escribo en pantalla lo que ya llevo
                          TextView textoSec = (TextView)findViewById(R.id.textoSecundario);  
                          textoSec.setText(LineaComando);  
                 }  
                 Numero = savedInstanceState.getString("Numero");  
                 if(Numero!="")  
                 {  
                          TextView texto = (TextView)findViewById(R.id.textoPrincipal);  
                          texto.setText(Numero);  
                 }  
           }  
           else  
           {
                //Si no hay datos guardados inicializo a 0
                LineaComando = "";  
                Numero = "";  
                Operacion = 0;  
                Resultado = (double) 0;  
                Memoria = (double) 0;  
           }  
      }  

Con esto ya tenemos lo que necesito para el ejercicio de la calculadora, pero vamos a rizar el rizo ya que es algo fácil.

Paso tres: Hacer que tambien recupere datos al restaurar la UI (cuando vuelva de una llamada por ejemplo)
Bueno, esto es tan sencillo como crear un metodo a posta para ese caso, que va a tener mas o menos lo mismo que el onCreate:

       @Override  
      public void onRestoreInstanceState(Bundle savedInstanceState) {  
       super.onRestoreInstanceState(savedInstanceState);  
       // Restaura la UI con los datos guardados  
       Resultado = savedInstanceState.getDouble("Resultado");  
       Memoria = savedInstanceState.getDouble("Memoria");  
       if(Memoria!=(double)0)  
       {  
                TextView M = (TextView)findViewById(R.id.Memoria);  
                M.setVisibility(View.VISIBLE);  
       }  
       Operacion = savedInstanceState.getInt("Operacion");  
       LineaComando = savedInstanceState.getString("LineaComando");  
       if(LineaComando.length()>0)  
       {  
                TextView textoSec = (TextView)findViewById(R.id.textoSecundario);  
                textoSec.setText(LineaComando);  
       }  
       Numero = savedInstanceState.getString("Numero");  
       if(Numero!="")  
       {  
         TextView texto = (TextView)findViewById(R.id.textoPrincipal);  
         texto.setText(Numero);  
       }  
      }  

 

Appbar para pivot y cambio dinámico en código

Al desarrollar una aplicaci&amp;oacute;n con el control Pivot una de las primeras dudas que me surgio er

Al desarrollar una aplicación con el control Pivot una de las primeras dudas que me surgio era como hacer una Appbar que cambiara con cada vista, esto lo encontre fácilmente en la documentación de Microsoft, pero aun así voy a explicarlo.

Primero en el archivo App.xaml, debajo de <Application.Resources> debemos insertar algo como esto para cada sección del pivot. Es importante que la x:Key sea diferente (bueno eso es imprescindible) y a ser posible algo identificativo (como menuAppBar1).

<shell:ApplicationBar x:Key="AppBar1" IsVisible="True" IsMenuEnabled="True">  
       <shell:ApplicationBarIconButton IconUri="/Images/iconos/img1.png" Text="boton1" Click="ApplicationBarIconButton_Click11" />  
       <shell:ApplicationBarIconButton IconUri="/Images/iconos/img2.png" Text="boton2" Click="ApplicationBarIconButton_Click13" />  
       <shell:ApplicationBarIconButton IconUri="/Images/iconos/img3.png" Text="boton3" Click="ApplicationBarIconButton_Click14" />  
       <shell:ApplicationBarIconButton IconUri="/Images/iconos/img4.png" Text="boton4" Click="ApplicationBarIconButton_Click12" />  
       <shell:ApplicationBar.MenuItems>  
            <shell:ApplicationBarMenuItem Text="link1" Click="ApplicationMenuIconButton_Click11"/>  
            <shell:ApplicationBarMenuItem Text="link2" Click="ApplicationMenuIconButton_Click12"/>  
       </shell:ApplicationBar.MenuItems>  
</shell:ApplicationBar> 

Este ejemplo en concreto tiene 4 botones y dos enlaces para el desplegable. Los enlaces no son imprescindibles, si los borras hay que poner el IsMenuEnabled como false.

Una vez hecho esto, en el codigo de la página donde esta el pivot debemos poner este código:

private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)  
  {  
      switch (((Pivot)sender).SelectedIndex)  
     {  
         case 0:  
           ApplicationBar = ((ApplicationBar)Application.Current.Resources["AppBar1"]);  
           break;  
         case 1:  
           ApplicationBar = ((ApplicationBar)Application.Current.Resources["AppBar2"]);  
           break;  
         case 2:  
           ApplicationBar = ((ApplicationBar)Application.Current.Resources["AppBar3"]);  
           break;  
         case 3:  
           ApplicationBar = ((ApplicationBar)Application.Current.Resources["AppBar4"]);  
           break;  
         case 4:  
           ApplicationBar = ((ApplicationBar)Application.Current.Resources["AppBar5"]);  
           break;  
      }  
 }  

Esta función lo que hace es, dependiendo de que pestaña del pivot esta seleccionada, carga uno de los fragmentos de codigo del App.xaml (en el ejemplo 4 secciones de pivot con 4 secciones en App.xaml) y por tanto la appbar que hemos definido antes. Para esto hay que recordar añadir el metodo Pivot_SelectionChanged a nuestro pivot.

Ahora bien, que pasa si como es mi caso, quieres que el texto o el icono cambien en función de comportamientos en el código, por ejemplo, quieres que un boton con un pulgar ponga "Me gusta", pero cambie a "Ya no me gusta" cuando es pulsado.

Bien, para empezar podemos llamar a la appbar y asignarla a una variable de esta manera:

var appBar = App.Current.Resources["localAppBar1"] as ApplicationBar;

Esto es especialmente útil porque podemos hacerlo en cualquier sitio, no necesariamente en la misma página donde esta la appbar.

Ahora podemos llamar a cualquier elemento de la barra como si fuera un array, por ejemplo algo asi:

(ApplicationBarIconButton)appBar.Buttons[1]).Text 

Con la idea del me gusta que tenia podria quedar algo tal que así:

 var appBar = App.Current.Resources["localAppBar1"] as ApplicationBar;  
 if (((ApplicationBarIconButton)appBar.Buttons[1]).Text == "Me Gusta")  
 {  
      //cambia a no me gusta  
 }  
 else  
 {  
     //cambia de nuevo a me gusta  
 }

Hay que recordar que los array empiezan en 0 (si... es una tonteria, pero es LA tipica tonteria), asi que en este ejemplo estariamos modificando el segundo boton (empezando por la izquierda).
Agradecimiento especial a Stackoverflow por la ayuda.