Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to create a custom picker in Xamarin Forms

I'm trying to create a custom picker with 3 components. I followed the tutorial on the Xamarin site here. I got the control to work, except when I click the Done button on the picker (This is the default done button). I get the following exception:

System.InvalidCastException: Unable to cast object of type 'myproj.iOS.MyPickerModel' to type 'Xamarin.Forms.Platform.iOS.PickerRenderer+PickerSource'.

Here is my code:

In the shared proj:

public class MyPicker: Picker {}

In the iOS proj:

[assembly: ExportRenderer(typeof(MyPicker), typeof(MyPickerRenderer))]

public class MyPickerRenderer: PickerRenderer
{
    // Override the OnElementChanged method so we can tweak this renderer post-initial setup
    protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {   
            var picker = (UIPickerView)this.Control.InputView;
            picker.BackgroundColor = UIColor.White;
            picker.Model = new MyPickerModel();
        }

And the Model:

public class MyPickerModel : UIPickerViewModel
{
    private string[] array1  = new string [] {
        "1","2","3","4"
    };
    private string[] array2  = new string [] {
        "1a","2a","3a","4a"
    };

    private string[] array3  = new string [] {
        "a","b","c","d"
    };

    public override nint GetComponentCount (UIPickerView pickerView)
    {
        return 3;
    }

    public override nint GetRowsInComponent (UIPickerView pickerView, nint component)
    {
        // Returns
        switch (component) {
            case 0: return array1.Length;
            case 1: return array2.Length;
            case 2: return array3.Length;
            default:break;
        }
        return 0;
    }

    public override string GetTitle (UIPickerView pickerView, nint row, nint component)
    {
        // Returns
        switch (component)
        {
            case 0: return array1[row];
            case 1: return array2[row];
            case 2: return array3[row];
            default: break;
        }
        return null;
    }

    public override nfloat GetComponentWidth (UIPickerView pickerView, nint component)
    {
        switch (component)
        {
            case 0: return 100.0f;
            case 1: return 100.0f;
            case 2: return 100.0f;
            default: break;
        }

        return 0;
    }

    public override nfloat GetRowHeight (UIPickerView pickerView, nint component)
    {
        return 40f;
    }
}

And lastly my page:

public class MyPage : ContentPage
{
    public MyPage()
    {
        MyPicker picker = new MyPicker
        {
            Title = "Color",
            VerticalOptions = LayoutOptions.CenterAndExpand
        };

        picker.SelectedIndexChanged += (sender, args) =>
        {

        };

        var mainLayout = new StackLayout
        {
            Orientation = StackOrientation.Vertical,
            Children =
            {
                picker

            }
        };


        // Accomodate iPhone status bar.
        this.Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);
        this.BackgroundImage = "background.png";

        // Build the page.
        this.Content = mainLayout;
    }
}

I don't understand why its trying to cast MyPickerModel to PickerRenderer+PickerSource

Thanks!

like image 868
yannify Avatar asked Mar 28 '15 04:03

yannify


People also ask

How do I create a dropdown in xamarin?

To do this, go to New - Project - Xamarin Cross-Platform App and then click OK to confirm. 2- Then, I select Android and iOS platforms. 3- The dropdown view should be selected in Xamarin.


2 Answers

You were almost there...Just create a new instance of the UIPickerView, assign your custom model (MyPickerModel) to it and then assign the UIPickerView to Control.InputView. OnElementChanged in your renderer would then look like this:

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

    if (Control != null)
    {
        var myPickerModel = new MyPickerModel();
        var myPickerView = new UIPickerView
        {
            Model = myPickerModel
        };
        //Assign your picker view
        Control.InputView = myPickerView;

        //You can also create an event in your model to react on user actions like e.g. "SelectedValueChanged"...
        myPickerModel.SelectedValueChanged += (sender, eventArgs) =>
        {
            //...to set the selected value as title of the picker element
            e.NewElement.Title = myPickerModel.SelectedValue;
        };
    }
}
like image 177
M. Hartner Avatar answered Oct 24 '22 16:10

M. Hartner


Not sure how IOS works but looks like my android version has shows what you need:

My basePicker looks like this:

    public class BaseBindablePicker : Picker
{
    public static readonly BindableProperty BackgroundColor=
    BindableProperty.Create<BaseBindablePicker, Color>(p => p.BackgroundColor, Color.Default);

    public Color ColorBackground
    {
        get { return (Color)base.GetValue(BackgroundColor); }
        set { base.SetValue(BackgroundColor, value); }
    }
}

My platform specific view looks like:

[assembly: ExportRenderer(typeof(BaseBindablePicker), typeof(SimpleEID.Android.Shared.CustomRender.AndroidPicker))]

public class AndroidPicker : PickerRenderer
{    
    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Picker> e)
    {
        base.OnElementChanged(e);

        var pick = (BaseBindablePicker)this.Element;
        this.Control.SetBackgroundColor(pick.BackColor.ToAndroid());
    }
}

Basicly looks like u need to change your

var picker = (UIPickerView)this.Control.InputView;

to be

var picker = (MyPicker)this.Element;

Then just refence what you need from there maybe add a reference to your model

like image 34
SatanEnglish Avatar answered Oct 24 '22 15:10

SatanEnglish