Skip to content

Instantly share code, notes, and snippets.

@softlion
Created April 2, 2016 08:17
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save softlion/fdc7c196a2d0461896b49835fd7cbef2 to your computer and use it in GitHub Desktop.
Save softlion/fdc7c196a2d0461896b49835fd7cbef2 to your computer and use it in GitHub Desktop.
mvvmcross MvxSpinner support for material design (using AppCompatSpinner)
using System;
using System.Collections;
using System.Windows.Input;
using Android.Content;
using Android.Runtime;
using Android.Support.V7.Widget;
using Android.Util;
using MvvmCross.Binding.Attributes;
using MvvmCross.Binding.Droid.Views;
namespace Droid.Lib.Ui
{
[Register("MvxSpinner2")]
public class MvxSpinner2 : AppCompatSpinner
{
public MvxSpinner2(Context context, IAttributeSet attrs)
: this(context, attrs,
new MvxAdapter(context)
{
SimpleViewLayoutId = global::Android.Resource.Layout.SimpleDropDownItem1Line
})
{
}
public MvxSpinner2(Context context, IAttributeSet attrs, IMvxAdapter adapter)
: base(context, attrs)
{
var itemTemplateId = MvxAttributeHelpers.ReadListItemTemplateId(context, attrs);
var dropDownItemTemplateId = MvxAttributeHelpers.ReadDropDownListItemTemplateId(context, attrs);
adapter.ItemTemplateId = itemTemplateId;
adapter.DropDownItemTemplateId = dropDownItemTemplateId;
this.Adapter = adapter;
this.SetupHandleItemSelected();
}
protected MvxSpinner2(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public new IMvxAdapter Adapter
{
get { return base.Adapter as IMvxAdapter; }
set
{
var existing = this.Adapter;
if (existing == value)
return;
if (existing != null && value != null)
{
value.ItemsSource = existing.ItemsSource;
value.ItemTemplateId = existing.ItemTemplateId;
}
if (existing != null)
{
existing.ItemsSource = null;
}
base.Adapter = value;
}
}
[MvxSetToNullAfterBinding]
public IEnumerable ItemsSource
{
get { return this.Adapter.ItemsSource; }
set { this.Adapter.ItemsSource = value; }
}
public int ItemTemplateId
{
get { return this.Adapter.ItemTemplateId; }
set { this.Adapter.ItemTemplateId = value; }
}
public int DropDownItemTemplateId
{
get { return this.Adapter.DropDownItemTemplateId; }
set { this.Adapter.DropDownItemTemplateId = value; }
}
public ICommand HandleItemSelected { get; set; }
public ICommand HandleItemSelectedIndex { get; set; }
private void SetupHandleItemSelected()
{
base.ItemSelected += (sender, args) =>
{
var position = args.Position;
this.HandleSelected(position);
};
}
protected virtual void HandleSelected(int position)
{
if (HandleItemSelected != null)
{
var item = Adapter.GetRawItem(position);
if (HandleItemSelected.CanExecute(item))
HandleItemSelected.Execute(item);
}
if (HandleItemSelectedIndex != null && HandleItemSelectedIndex.CanExecute(position))
HandleItemSelectedIndex.Execute(position);
}
}
}
using System;
using Android.Widget;
using MvvmCross.Binding;
using MvvmCross.Binding.Bindings.Target.Construction;
using MvvmCross.Binding.Droid.Target;
using MvvmCross.Platform.Platform;
namespace Droid.Lib.Ui
{
public class MvxSpinner2SelectedItemBinding : MvxAndroidTargetBinding
{
protected MvxSpinner2 Spinner => (MvxSpinner2)Target;
private object _currentValue;
private bool _subscribed;
public static void Register(IMvxTargetBindingFactoryRegistry registry)
{
registry.RegisterCustomBindingFactory<MvxSpinner2>("SelectedItem", spinner => new MvxSpinner2SelectedItemBinding(spinner));
}
public MvxSpinner2SelectedItemBinding(MvxSpinner2 spinner)
: base(spinner)
{
}
private void SpinnerItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
var spinner = this.Spinner;
if (spinner == null)
return;
var newValue = spinner.Adapter.GetRawItem(e.Position);
bool changed;
if (newValue == null)
{
changed = (this._currentValue != null);
}
else
{
changed = !(newValue.Equals(this._currentValue));
}
if (!changed)
{
return;
}
this._currentValue = newValue;
FireValueChanged(newValue);
}
protected override void SetValueImpl(object target, object value)
{
var spinner = (MvxSpinner2)target;
if (value == null)
{
MvxBindingTrace.Warning("Null values not permitted in spinner SelectedItem binding currently");
return;
}
if (!value.Equals(this._currentValue))
{
var index = spinner.Adapter.GetPosition(value);
if (index < 0)
{
MvxBindingTrace.Trace(MvxTraceLevel.Warning, "Value not found for spinner {0}", value.ToString());
return;
}
this._currentValue = value;
spinner.SetSelection(index);
}
}
public override MvxBindingMode DefaultMode => MvxBindingMode.TwoWay;
public override void SubscribeToEvents()
{
var spinner = this.Spinner;
if (spinner == null)
return;
spinner.ItemSelected += this.SpinnerItemSelected;
this._subscribed = true;
}
public override Type TargetType => typeof(object);
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
var spinner = this.Spinner;
if (spinner != null && this._subscribed)
{
spinner.ItemSelected -= this.SpinnerItemSelected;
this._subscribed = false;
}
}
base.Dispose(isDisposing);
}
}
}
using System;
using Android.Widget;
using MvvmCross.Binding;
using MvvmCross.Binding.Bindings.Target.Construction;
using MvvmCross.Binding.Droid.Target;
using MvvmCross.Platform.Platform;
namespace Droid.Lib.Ui
{
public class MvxSpinner2SelectedItemIndexBinding : MvxAndroidTargetBinding
{
protected MvxSpinner2 Spinner => (MvxSpinner2)Target;
private int _currentValue;
private bool _subscribed;
public static void Register(IMvxTargetBindingFactoryRegistry registry)
{
registry.RegisterCustomBindingFactory<MvxSpinner2>("SelectedItemIndex", spinner => new MvxSpinner2SelectedItemIndexBinding(spinner));
}
public MvxSpinner2SelectedItemIndexBinding(MvxSpinner2 spinner)
: base(spinner)
{
}
private void SpinnerItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
var spinner = this.Spinner;
if (spinner == null)
return;
var newValue = e.Position;
var changed = newValue != _currentValue;
if (!changed)
{
return;
}
this._currentValue = newValue;
FireValueChanged(newValue);
}
protected override void SetValueImpl(object target, object value)
{
var spinner = (MvxSpinner2)target;
if (value == null)
{
MvxBindingTrace.Warning("Null values not permitted in spinner SelectedItemIndex binding currently");
return;
}
if (!value.Equals(this._currentValue))
{
var index = Convert.ToInt32(value);
if (index < 0)
{
MvxBindingTrace.Trace(MvxTraceLevel.Warning, $"Index < 0 for spinner: {index}");
return;
}
this._currentValue = index;
spinner.SetSelection(index);
}
}
public override MvxBindingMode DefaultMode => MvxBindingMode.TwoWay;
public override void SubscribeToEvents()
{
var spinner = this.Spinner;
if (spinner == null)
return;
spinner.ItemSelected += this.SpinnerItemSelected;
this._subscribed = true;
}
public override Type TargetType => typeof(int);
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
var spinner = this.Spinner;
if (spinner != null && this._subscribed)
{
spinner.ItemSelected -= this.SpinnerItemSelected;
this._subscribed = false;
}
}
base.Dispose(isDisposing);
}
}
}

In setup.cs add:

    protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
    {
        base.FillTargetFactories(registry);
        MvxSpinner2SelectedItemIndexBinding.Register(registry);
        MvxSpinner2SelectedItemBinding.Register(registry);
    }

Then use the spinner as you would use the native MvxSpinner:

<MvxSpinner2
    local:MvxBind="ItemsSource Items;SelectedItemIndex SelectedItemIndex"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
     />

Note that this spinner supports a new SelectedItemIndex binding in addition to the traditional SelectedItem binding. This is useful for simple collections, as expected in spinners.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment