Forum Xamarin.Android

How to create a Custom Picker that displays the same alert dialog as the standard Picker

I needed to create a custom picker for Android with rounded corners so I created a CustomPicker class and a CustomPickerRenderer class. The Picker now displays the way I want it but when I click it the alert / selection dialog looks nothing like the dialog that the standard Picker uses. I would have thought that I was extending the Picker class and that it should appear the same. How do I get the dialog to look like the standard?

CustomPickerRenderer.cs

using Android.Content;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using AdvancedMD.Mobile.Connect.CustomControls;
using Android.Support.V4.Content.Res;
using AdvancedMD.Mobile.Connect.Droid.CustomControls;

[assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))]
namespace AdvancedMD.Mobile.Connect.Droid.CustomControls {

public class CustomPickerRenderer : PickerRenderer {

public CustomPickerRenderer(Context context) : base(context) {
}

protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) {
  base.OnElementChanged(e);
  if (Control != null) {
    Control.SetBackground(ResourcesCompat.GetDrawable(Resources, Resource.Layout.RoundedCornerEntry, null));
  }
}

}
}

CaptureChallengeSettingsView.xaml (excerpt)

      <controls:CustomPicker Title="Select Security Question" TitleColor="#93A3B3" TextColor="White"
              HorizontalOptions="FillAndExpand" BackgroundColor="#476075"
              ItemsSource="{Binding Selections}" SelectedItem="{Binding Question1SelectedItem}" />
      <Picker Title="Basic Picker" TitleColor="#93A3B3" TextColor="White"
              HorizontalOptions="FillAndExpand" BackgroundColor="#476075"
              ItemsSource="{Binding Selections}" SelectedItem="{Binding Question1SelectedItem}" />

Custom rendered Picker:

Standard Picker:

Best Answer

  • dapiggdapigg Member
    Accepted Answer

    It looks like the base Picker for Android uses Xamarin.Forms.Platform.Android.AppCompat so I resolved this by using the IPickerRenderer.onClick() code and implementing it in my custom renderer to get the same look and feel.

    void IPickerRenderer.OnClick() {
      CustomPicker model = (CustomPicker)Element;
      if (_dialog == null) {
        using (var builder = new AlertDialog.Builder(Context)) {
          if (!Element.IsSet(CustomPicker.TitleColorProperty)) {
            builder.SetTitle(model.Title ?? "");
          }
          else {
            var title = new SpannableString(model.Title ?? "");
            title.SetSpan(new ForegroundColorSpan(model.TitleColor.ToAndroid()), 0, title.Length(), SpanTypes.ExclusiveExclusive);
    
            builder.SetTitle(title);
          }
    
          string[] items = model.Items.ToArray();
          builder.SetItems(items, (s, e) => ((IElementController)model).SetValueFromRenderer(CustomPicker.SelectedIndexProperty, e.Which));
    
          builder.SetNegativeButton(global::Android.Resource.String.Cancel, (o, args) => { });
    
          ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
    
          _dialog = builder.Create();
        }
        _dialog.SetCanceledOnTouchOutside(true);
        _dialog.DismissEvent += (sender, args) => {
          (Element as IElementController)?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
          _dialog.Dispose();
          _dialog = null;
        };
    
        _dialog.Show();
      }
    }
    
    protected override void OnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e) {
      base.OnFocusChangeRequested(sender, e);
    
      if (e.Focus) {
        if (Clickable)
          CallOnClick();
        else
          ((IPickerRenderer)this)?.OnClick();
      }
      else if (_dialog != null) {
        _dialog.Hide();
        ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
        Control.ClearFocus();
        _dialog = null;
      }
    }
    

Answers

  • jezhjezh Member, Xamarin Team Xamurai

    Based on my test, when we used the PickerRenderer without adding other code, it still has such result.So it should be the Picker renderer have changed the Picker style.

     public class CustomPickerRenderer : PickerRenderer
        {
            public CustomPickerRenderer(Context context) : base(context)
            {
            }
        }
    
  • dapiggdapigg Member
    Accepted Answer

    It looks like the base Picker for Android uses Xamarin.Forms.Platform.Android.AppCompat so I resolved this by using the IPickerRenderer.onClick() code and implementing it in my custom renderer to get the same look and feel.

    void IPickerRenderer.OnClick() {
      CustomPicker model = (CustomPicker)Element;
      if (_dialog == null) {
        using (var builder = new AlertDialog.Builder(Context)) {
          if (!Element.IsSet(CustomPicker.TitleColorProperty)) {
            builder.SetTitle(model.Title ?? "");
          }
          else {
            var title = new SpannableString(model.Title ?? "");
            title.SetSpan(new ForegroundColorSpan(model.TitleColor.ToAndroid()), 0, title.Length(), SpanTypes.ExclusiveExclusive);
    
            builder.SetTitle(title);
          }
    
          string[] items = model.Items.ToArray();
          builder.SetItems(items, (s, e) => ((IElementController)model).SetValueFromRenderer(CustomPicker.SelectedIndexProperty, e.Which));
    
          builder.SetNegativeButton(global::Android.Resource.String.Cancel, (o, args) => { });
    
          ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
    
          _dialog = builder.Create();
        }
        _dialog.SetCanceledOnTouchOutside(true);
        _dialog.DismissEvent += (sender, args) => {
          (Element as IElementController)?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
          _dialog.Dispose();
          _dialog = null;
        };
    
        _dialog.Show();
      }
    }
    
    protected override void OnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e) {
      base.OnFocusChangeRequested(sender, e);
    
      if (e.Focus) {
        if (Clickable)
          CallOnClick();
        else
          ((IPickerRenderer)this)?.OnClick();
      }
      else if (_dialog != null) {
        _dialog.Hide();
        ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
        Control.ClearFocus();
        _dialog = null;
      }
    }
    
  • jezhjezh Member, Xamarin Team Xamurai

    Congrats, and thanks for sharing the answer. Could you please mark it as an answer so that it could help others who have the similar question? Thanks.

  • LyndonHugheyLyndonHughey USUniversity ✭✭✭

    Thank you for this. You saved me lots of time and frustration!

Sign In or Register to comment.