Twitter Facebook RSS Feed

miércoles, 11 de diciembre de 2019 a las 11:50hs por Gustavo Cantero (The Wolf)

Uno de los problemas con los que nos topamos al desarrollar un formulario con Xamarin Forms es que, a diferencia del control Entry, no existe la propiedad Placeholder en el DatePicker para identificar qué se está ingresando en ese campo.
Por suerte los controles nativos de Android e iOS tienen opciones para esto, por lo cual, sólo tuvimos que hacer un control que herede del DatePicker original y crear un renderer para cada plataforma, para implementar esta propiedad.

Para comenzar, creamos el control que herede del DatePicker de Xamarin Forms, para agregar la nueva propiedad:

using Xamarin.Forms;

namespace MiProyecto.Controls
{
    public class MyDatePicker : Xamarin.Forms.DatePicker
    {
        public static readonly BindableProperty EnterTextProperty =
            BindableProperty.Create(propertyName: nameof(Placeholder), returnType: typeof(string), declaringType: typeof(MyDatePicker), defaultValue: default(string));

        public string Placeholder { get; set; }
    }
}

Este control lo podemos utilizar de la siguiente forma:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:controls="clr-namespace:MiProyecto.Controls"
             mc:Ignorable="d">
    <controls:DatePicker Placeholder="Fecha de nacimiento"
                         Visual="Material"
                         Format="D"
                         VerticalOptions="Center" />
</ContentPage>

Ahora deberemos crear el Renderer en cada plataforma. Nótese que nosotros utilizamos el Visual Material, con lo cual, deberemos heredar de la clase MaterialDatePickerRenderer.

Android

Primero crearemos el renderer para Android, cuyo control nativo, el TextInputLayout, tiene la propiedad Hint para que hace lo mismo que el Placeholder.
Este renderer tendremos que crearlo en el proyecto de Android, para poder tener acceso a los controles nativos.

using Android.App;
using Android.Content;
using Xamarin.Forms;
using Xamarin.Forms.Material.Android;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(MiProyecto.Controls.MyDatePicker), typeof(MiProyecto.Droid.Renderer.DatePickerRenderer), new[] { typeof(VisualMarker.MaterialVisual) })]
namespace MiProyecto.Droid.Renderer
{
    class DatePickerRenderer : MaterialDatePickerRenderer
    {
        public DatePickerRenderer(Context context) : base(context) { }

        protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
        {
            base.OnElementChanged(e);

            //Placeholder
            if (Element is Controls.MyDatePicker picker && !string.IsNullOrWhiteSpace(picker.Placeholder))
            {
                Control.HintEnabled = true;
                Control.Hint = picker.Placeholder;
            }
        }
    }
}

iOS

Ahora tenemos que hacer lo mismo, pero para iOS. En este caso, el control también debe heredar de la interfaz IMaterialEntryRenderer, que tiene las propiedades para mostrar el placeholder, pero que el control DatePicker de Xamarin blanquea.
Al igual que con Android, este renderer tendremos que crearlo en el proyecto de iOS.

using Xamarin.Forms;
using Xamarin.Forms.Material.iOS;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(MiProyecto.Controls.MyDatePicker), typeof(MiProyecto.iOS.Renderer.DatePickerRenderer), new[] { typeof(VisualMarker.MaterialVisual) })]
namespace MiProyecto.iOS.Renderer
{
    class DatePickerRenderer : MaterialDatePickerRenderer, IMaterialEntryRenderer
    {
        public DatePickerRenderer() : base() { }

        string IMaterialEntryRenderer.Placeholder
        {
            get
            {
                if (Element is Controls.MyDatePicker picker && !string.IsNullOrWhiteSpace(picker.Placeholder))
                    return picker.Placeholder;
                return string.Empty;
            }
        }
    }
}

Con estos renderers, veremos el DatePicker en iOS 13.3 y Android 10.0 como se muestra a continuación.

Espero les sirva este artículo.
¡Suerte!

5 comentarios »

  1. Jérôme Liger dice:

    Ola ! Muy interessante, gracias.
    Hecho lo mismo Renderer, pero tengo que ir mas lejo… necessito a cambiar el color de este PlaceHolder.
    Cree una BindableProperty “PlaceHolderColor” en mi customDatePicker, pero en AndroidRenderer no puedo assignar este color…

    Intente Control.DefaultHintTextColor = new ColorStateList(states, colors);
    Y functionna… pero cuando selectionno una fecha, el color desaparece… no sé porque.

    Una idea ? una pista ?

    Muchas gracias (y disculpame para mi espanol… muchos anos que no he practicado ;-))

    • Hola Jérôme.
      ¿Probaste de cambiar el color con el método SetHintTextColor?
      Saludos.

      • Jérôme Liger dice:

        El “Control” es de typo “MaterialPickerTextInputLayout” y no tiene este método SetHintTextColor (pero solo DefaultHintTextColor propriedad que functionna pour el primero display, pero no despues…)
        He encontrado el “Control.EditText” que tienne este método “SetHintTextColor” pero no functiona en absoluto.

        Tambien intenté interceptar el control nativo “MaterialPickerTextInputLayout” y de mofificar esto (al lugar del “Control”) pero no mejor…

        MaterialPickerTextInputLayout InputLayout;
        protected override MaterialPickerTextInputLayout CreateNativeControl()
        {
            InputLayout = base.CreateNativeControl();
            return InputLayout;
        }
        
        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            [...]
        
            InputLayout.HintEnabled = true;
            InputLayout.Hint = formsPlaceHolder;
            InputLayout.DefaultHintTextColor = new ColorStateList(states, colors); //StateList constructed from FormsPlaceHolderColor
            InputLayout.EditText.SetHintTextColor(new ColorStateList(states, colors));
        }
        • Jérôme Liger dice:

          Ok, lo encontré yo mismo pero tenía que saberlo… si necessita alguien…

          El “InputLayer” (Control de typo MaterialPickerTextInputLayout) tienne un método “ApplyTheme(textcolor, hintcolor)” que functionna.
          El completo renderer es :

          [assembly: ExportRenderer(typeof(CustomMaterialDatePicker), typeof(AndroidMaterialDatePickerRenderer), new[] { typeof(VisualMarker.MaterialVisual) })]
          namespace YourAndroidRendererNamespace
          {
              public class AndroidMaterialDatePickerRenderer : MaterialDatePickerRenderer
              {
          
                  public AndroidMaterialDatePickerRenderer(Context context) : base(context) { }
          
                  MaterialPickerTextInputLayout InputLayout;
                  protected override MaterialPickerTextInputLayout CreateNativeControl()
                  {
                      InputLayout = base.CreateNativeControl();
                      return InputLayout;
                  }
          
                  protected override void OnElementChanged(ElementChangedEventArgs e)
                  {
                      base.OnElementChanged(e);
                      if (e.NewElement == null) return;
          
                      var custompicker = e.NewElement as CustomMaterialDatePicker;
                      if (InputLayout != null)
                      {
                          InputLayout.HintEnabled = true;
                          InputLayout.Hint = custompicker.PlaceHolder;
                          InputLayout.ApplyTheme(custompicker.TextColor, custompicker.PlaceHolderColor);
                      }
                  }
              }
          }

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.