0

So, the question is simple. How do I apply styling for MvxSpinner.

There is plenty of answers regarding to styling a common Spinner, but most of them refer to this lines of code:

var adapter = new ArrayAdapter<string>(SupportActionBar.ThemedContext, Resource.Layout.event_selector, events);
  adapter.SetDropDownViewResource(global::Android.Resource.Layout.SimpleSpinnerDropDownItem);

_eventSelector.Adapter = adapter;

However, I am not sure how to handle that in case of MvxSpinner. It probably uses some MvvmCross-specific own adapter. My binding is as follows:

 <Mvx.MvxSpinner
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/lstCategoryGroups"
    local:MvxBind="ItemsSource CategoryGroups; SelectedItem CategoryGroupdSelected"
     />

Any ideas? Both approaches: as styling the specific spinner element on the page as setting common style for any spinner. Thanks!

Agat
  • 4,577
  • 2
  • 34
  • 62

1 Answers1

4

You can change the layout of the spinner (hence the style) directly in axml via two attributes:

  • local:MvxItemTemplate: Sets the layout of the selected item.
  • local:MvxDropDownItemTemplate: Sets the layout of each item in the dropdown

So given a ViewModel with a collection CategoryGroups and the SelectedCategoryGroup, i.e.:

public class MyViewModel : MvxViewModel
{
    ...

    public ObservableCollection<CategoryGroup> CategoryGroups { get; set; }

    public CategoryGroup CategoryGroupdSelected { get; set; }

    ...
}

and the CategoryGroup class:

public class CategoryGroup
{
    public int Id { get; set; }

    public string Name { get; set; }
}

You can bind the collection to a spinner like this:

<MvxSpinner
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="2"
    android:theme="@style/Spinner"
    local:MvxItemTemplate="@layout/my_spinner_item"
    local:MvxDropDownItemTemplate="@layout/my_spinner_dropdown_item"
    local:MvxBind="ItemsSource CategoryGroups; SelectedItem CategoryGroupdSelected" />

and in each template you define the layout/style of the item. Take into account that in the templates their DataContext is an item (in this case a CategoryGroup). In my example, I want to display the Name of each CategoryGroup then I just need a TextView and bind its Text property to the Name of the CategoryGroup, i.e. the item's DataContext:

my_spinner_item.axml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="5dp"
    android:textColor="@android:color/black"
    android:textSize="25sp"
    local:MvxBind="Text Name" />

my_spinner_dropdown_item.axml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    style="?android:attr/spinnerDropDownItemStyle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="8dp"
    android:paddingBottom="8dp"
    android:textColor="@android:color/black"
    android:alpha="0.8"
    local:MvxBind="Text Name" />

Now if you want all of your spinner to have the same templates you can just set a style with those attributes or you can inherit MvxSpinner and set the templates programmatically.

Apart from that if you want to use one of each template and not create one per each different binding you could have a base class BaseItemViewModel with an abstract property named Description and use that to bind in the View. So that in your VMs you always inherit from BaseItemViewModel and override Description property to have the value you want to display.

public abstract class BaseItemViewModel : MvxNotifyPropertyChanged
{
    public abstract string Description { get; }
}

Another way is to have a WrappedItemViewModel where you pass the object T you want to be in the spinner and a Func<T, string> that returns what the spinner displays (the description) and you have a Description property that just invokes the Func<T, string>. This way has the advantage that it doesn't make you inherit always from a VM and you can wrap almost any object in it adding the behaviour to be in a Spinner.

public class WrappedItemViewModel<T> : MvxNotifyPropertyChanged
{
    private readonly Func<T, string> descriptionFunc;

    public WrappedItemViewModel(T item, Func<T, string> descriptionFunc)
    {
         this.MyItem = item;
         this.descriptionFunc = descriptionFunc;
    }

    public T MyItem { get; }

    public string Description => this.descriptionFunc.Invoke(this.MyItem);
}
fmaccaroni
  • 3,846
  • 1
  • 20
  • 35
  • Thanks for the quick response. However, could you also provide (possibly with update in the answer) details of the my_spinner_item template. I mean, for example, the TextView should be also bound to data in some way. I guess, it won't work without additional manipulations? (Especially, if I want that to be generic template for any Spinner). – Agat Jan 17 '18 at 14:51
  • @Agat there I've updated the answer with some ways to achieve what you want. HIH – fmaccaroni Jan 17 '18 at 17:16
  • That's still not fully clear regarding to ItemViewModel and TextValue property to bound. Still cannot get it to work. Could you clarify on that? So, let's say, if I have MyCurrentViewViewModel with a set of Products (simple strings or Product class with own properties, for instance), and also a ProductSelected property. So, I can't catch what's the TextValue property in all that routine. – Agat Jan 17 '18 at 19:47
  • I've updated the answer taking the `CategoryGroups` of your question. To your case of `Products`, `TextValue` would be a property inside a `Product`. I hope now it's clearer. – fmaccaroni Jan 17 '18 at 20:16