Wednesday, April 22, 2015

A simple ComboBox for MVVMCross (Xamarin)

Working on getting a "ComboBox" control for our current iOS project.  We are using the MVVMCross libraries by Stuart Lodge.  I am surprised at the relutance of the Apple/Windows to support a simple combo box.  The best iOS option appears to be the UIPickerView.  I found a large number of sample projects and code snippets for straight Objective C and even Xamarin/Monotouch projects. 

A Start...

I was looking for an implementation using the MVVMCross pattern.  I found something very close in https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/ApiExamples.  The touch project contains the FirstView.cs

Line 144:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var picker = new UIPickerView();
var pickerViewModel = new MvxPickerViewModel(picker);
picker.Model = pickerViewModel;
picker.ShowSelectionIndicator = true;
var textView = new UITextField(new RectangleF(10, 100, 300, 30));
Add(textView);
textView.InputView = picker;
var label = new UILabel(new RectangleF(10, 130, 300, 30));
Add(label);
var set = this.CreateBindingSet<SpinnerView, SpinnerViewModel>();
set.Bind(pickerViewModel).For(p => p.SelectedItem).To(vm => vm.SelectedItem);
set.Bind(pickerViewModel).For(p => p.ItemsSource).To(vm => vm.Items);
set.Bind(textView).To(vm => vm.SelectedItem);
set.Bind(label).To(vm => vm.SelectedItem);
set.Apply();
}

This gets us part of the way there but what this binding does is simply bring up the picker view and displays the 4 options in the Model.  The PickerView contains no way to exit out of the control except using the default back in the iOS navigation bar.  This is not going to work for my use case.  I needed to have a way to close out the pickerview once the selection had been made.

Getting Closer ...

Over at 13daysaweek blog I stumbled across the article Combobox Type Input With iOS and MonoTouch "Combobox Type Input With iOS and MonoTouch".  This is trying to solve the problem I was having but it was not using MVVM.  It was however critical to understanding that the UITextField has two properties which allow a user to associate the pickerview with the textview object.  The properties:
  • TextField.InputView
  • TextField.InputAccessoryView
These properties allow a user to associate the PickerView with the Textbox

"I noticed another interesting property on the UITextField class, InputAccessoryView. It turns out that if a view is assigned to this property, it will be displayed above the view specified by the InputView property. With that in mind, I created a UIToolBar, added a button and set that as the InputAccessorView. "

A Solution ...

So combining both the approaches yielded the following solution


public override void ViewDidLoad()
{
base.ViewDidLoad();
var picker = new UIPickerView();
var pickerViewModel = new MvxPickerViewModel(picker);
picker.Model = pickerViewModel;
picker.ShowSelectionIndicator = true;
var textView = new UITextField(new RectangleF(10, 100, 300, 30));
Add(textView);

UIToolbar = new UIToolbar();
toolbar.BarStyle = UIBarStyle.Black;
toolbar.Translucent = true;
toolbar.SizeToFit();
UIBarButtonItem doneButton = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done, (s, e) =>
{
   //This dismisses the picker view and returns control to the main screen.
   textView.ResignFirstResponder();
}
toolbar.SetItems(new UIBarButtonItem[] { doneButton }, true);

 //This associates the picker view with the textview          
textView.InputView = picker;

//This will insert the toolbar into the pickerview.  This will allow the user to dismiss the
//view once a choice has been made.
textView.InputAccessoryView = toolbar;
var set = this.CreateBindingSet<SpinnerView, SpinnerViewModel>();
set.Bind(pickerViewModel).For(p => p.SelectedItem).To(vm => vm.SelectedItem);
set.Bind(pickerViewModel).For(p => p.ItemsSource).To(vm => vm.Items);
set.Bind(textView).To(vm => vm.SelectedItem);
set.Bind(label).To(vm => vm.SelectedItem);
set.Apply();
}

The toolbar control has been added so that a user selecting from the picker view can close the view once they have selected the value.  This allows the control to be more standalone as it does not depend on the navigation bar as the original code base provided in the MVVM sample.  The critical element in the view is the remember to assign the ResignFirstResponder method to the doneButton action.  If you do not do this then the activation of the done button will not close the PickerView leaving the user stranded on the control.


Source:
  1. https://github.com/MvvmCross/MvvmCross-Tutorials/blob/master/ApiExamples/ApiExamples.Touch/Views/FirstView.cs
  2. http://thirteendaysaweek.com/2012/09/19/combobox-type-input-with-ios-and-monotouch/
  3. https://github.com/13daysaweek/MonoTouchUIPickerView
  4. http://forums.xamarin.com/discussion/5805/uipickerview-example

1 comment:

  1. Got a lot of tips and advice those solve existing Xamarin Developer issue and with possible best solution for a simple ComboBox for MVVMCross (Xamarin).

    Thank you for the giveaway!

    ReplyDelete