Xamarin UIPickerView as a Combobox

Comboboxes are one of the most common and useful controls in web and application design.  They are mainly used for selecting a single instance of an enum e.g. state a user lives in.

StateComboBox

There is no such control in iOS so we wire up a UIPickerView to achieve similar functionality.

Thanks to ThirteenDaysAWeek for the initial code which i have adapted slightly to my liking.  My version allows the same UIPickerView to be used on multiple fields plus a few minor tweeks.

The idea is you want a UIPickerView control to appear from the bottom when editing a textfield and have a done button which posts the data back to the UITextField upon completion.

StateIOS

Firstly add a new class to act as the data model for the UIPickerView.

public class PickerModel : UIPickerViewModel
{
    public IList<Object> values;

    public event EventHandler<PickerChangedEventArgs> PickerChanged;

    public PickerModel(IList<Object> values)
    {
        this.values = values;
    }

    public override int GetComponentCount (UIPickerView picker)
    {
        return 1;
    }

    public override int GetRowsInComponent (UIPickerView picker, int component)
    {
        return values.Count;
    }

    public override string GetTitle (UIPickerView picker, int row, int component)
    {
        return values[row].ToString ();
    }

    public override float GetRowHeight (UIPickerView picker, int component)
    {
        return 40f;
    }

    public override void Selected (UIPickerView picker, int row, int component)
    {
        if (this.PickerChanged != null)
        {
            this.PickerChanged(this, new PickerChangedEventArgs{SelectedValue = values[row]});
        }
    }
}

public class PickerChangedEventArgs : EventArgs{
    public object SelectedValue {get;set;}
}

Then add a global reference to both the picker and picker data model in your view controller where you want to use the UIPickerView.

PickerModel picker_model;
UIPickerView picker;

Populate your UIPIckerView data model in your view controller ViewDidLoad()

List<Object> state_list= new List<Object> ();
state_list.Add ("ACT");
state_list.Add ("NSW");
state_list.Add ("NT");
state_list.Add ("QLD");
state_list.Add ("SA");
state_list.Add ("TAS");
state_list.Add ("VIC");
state_list.Add ("WA");
picker_model = new PickerModel (state_list);

Create the UIPickerView in ViewDidLoad() and link the data model to it.

picker =  new UIPickerView ();
picker.Model = picker_model;
picker.ShowSelectionIndicator = true;

Create the toolbar and done button to appear next to the UIPickerView also in ViewDidLoad().  The button clicked Lambda expression finds which UITextField opened the picker and sets its value to the selected picker value and closes it.

UIToolbar toolbar = new UIToolbar ();
toolbar.BarStyle = UIBarStyle.Black;
toolbar.Translucent = true;
toolbar.SizeToFit ();

UIBarButtonItem doneButton = new UIBarButtonItem("Done",UIBarButtonItemStyle.Done,(s,e) =>
{
    foreach (UIView view in this.View.Subviews) 
    {
        if (view.IsFirstResponder)
        {
            UITextField textview = (UITextField)view;
            textview.Text = picker_model.values[picker.SelectedRowInComponent (0)].ToString ();
            textview.ResignFirstResponder ();
        }
    }

});
toolbar.SetItems (new UIBarButtonItem[]{doneButton},true);

Now wire everything up in ViewDidLoad().  If you didn’t know the InputView is a way of setting a non default view for editing content.  Normally when you clicked a UITextField the keyboard would appear.  In this code we specify that the UIPickerView should perform the editing instead.  The InputAccessoryView allows you to add more controls above the keyboard… or in this case above the UIPIckerView.

txt_State_1.InputView = picker;
txt_State_1.InputAccessoryView = toolbar;
txt_State_2.InputView = picker;
txt_State_2.InputAccessoryView = toolbar;

I can add the picker and toolbar to as many UITextFields as i want.

A few improvments:

We want the picker to pre select the correct item when we click on a field and not just start at the top or the last selected item.  We add a Set Picker function to the touchdown event of the UITextField:

this.txt_State_1.TouchDown += SetPicker;
this.txt_State_2.TouchDown += SetPicker;
this.txt_State_3.TouchDown += SetPicker;
private void SetPicker(object sender, EventArgs e)
{
    UITextField field = (UITextField)sender;
    picker.Select (picker_model.values.IndexOf (field.Text), 0, true);
}