Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to highlight the selected item in an MvxListView

How can I keep an item, in an MvxListView, highlighted until it is unselected or until another item is selected?

My program has an MvxListView that correctly displays a list of items. The user can select an item, by clicking it, and then click a save button. The selected item is stored in MyChosenItem until it is needed by the save button code. Currently, the selected item stays highlights for a split second before returning to the unselected color.

This is how the MvxListView is created:

<Mvx.MvxListView
    android:layout_width="match_parent"
    android:layout_height="260dp"
    android:layout_marginTop="40dp"
    android:id="@+id/MyMvxListViewControl"
    local:MvxBind="ItemsSource MyItems; SelectedItem MyChosenItem"
    local:MvxItemTemplate="@layout/my_item_layout" />

This is Layout/my_item_layout.xaml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res/Project.Ui.Droid"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <TextView
        android:layout_width="300.0dp"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:textSize="20dp"
        android:textColor="#000000"
        local:MvxBind="Text Field1" />
    <TextView
        android:layout_width="250.0dp"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:textSize="20dp"
        android:textColor="#000000"
        local:MvxBind="Text Field2" />
</LinearLayout>
like image 475
Josh Avatar asked Aug 21 '13 18:08

Josh


1 Answers

This method provides an easy way to customize which items remain highlighted. I settled on this because it gives me full control over what is highlighted and how it is displayed in the list. (This example shows highlighting only one item, but it could easily be extended to highlight more.)

  1. The MvxListView, in the original question, links to MyItems and MyChosenItem in the associated view model. MyItems is a collection of Item, and MyChosenItem is just a single Item. I added isItemSelected to Item. The Item class looks like this now:

    public class Item : MvxViewModel           
    {
        private string _field1;
        private string _field2;
        private bool _isItemSelected = false;
    
        public string Field1
        {
            get { return _field1; }
            set
            {
                _field1= value;
                RaisePropertyChanged("Field1");
            }
        }
    
        public string Field2
        {
            get { return _field2; }
            set
            {
                _field2= value;
                RaisePropertyChanged("Field2");
            }
        }
    
        public bool isItemSelected
        {
            get { return _isItemSelected; }
            set
            {
                _isItemSelected = value;
                RaisePropertyChanged("isItemSelected");
            }
        }
    }
    

    Note: The Item class extends MvxViewModel so that RaisePropertyChange() can be called. This allows my_item_layout.xaml to be notified when that property changes.

  2. Update each instance of isItemSelected from the property that the MvxListView's SelectedItem binds to. In this case, that's the MyChosenItem property in the associated view model. This is what the new code looks like:

    public Item MyChosenItem
    {
        get { return _myChosenItem; }
        set
        {
            if (_myChosenItem != value)
            {
                _myChosenItem = value;
                UpdateItemSelections();
                RaisePropertyChanged("MyChosenItem");
            }
        }
    }
    
    // Select MyChosenItem and unselect all other items
    private void UpdateItemSelections()
    {
        if( MyItems.Count > 0)
        {
            for (int index = 0; index < MyItems.Count; index++)
            {
                // If the chosen item is the same, mark it as selected
                if (MyItems[index].Field1.Equals(MyChosenItem.Field1)
                    && MyItems[index].Field2.Equals(MyChosenItem.Field2))
                {
                    MyItems[index].isItemSelected = true;
                }
                else
                {
                    // Only trigger the property changed event if it needs to change
                    if (MyItems[index].isItemSelected)
                    {
                        MyItems[index].isItemSelected = false;
                    }
                }
            }
        }
    }
    

    It would be pretty easy to modify UpdateItemSelections() to any selection behavior you want.

  3. Make each row do something based on the isItemSelected property. I just made the background change colors by controlling the visibility property of a view. However, all sorts of things are possible. isItemSelected could even be passed to a custom control for some really interesting visuals. My new Layout/my_item_layout.xaml looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:local="http://schemas.android.com/apk/res/Project.Ui.Droid"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
    
        <!-- SELECTED BACKGROUND COLOR -->
        <View
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#FF0000"
            local:MvxBind="Visibility isItemSelected,Converter=BoolToViewStates" />
    
        <TextView
            android:layout_width="300.0dp"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:textSize="20dp"
            android:textColor="#000000"
            local:MvxBind="Text Field1" />
        <TextView
            android:layout_width="250.0dp"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:textSize="20dp"
            android:textColor="#000000"
            local:MvxBind="Text Field2" />
    </LinearLayout>
    

EDIT

It might be better to use an MvxCommand instead of triggering the highlighted action when the SelectedItem is set. It seems the SelectedItem is only set if it is not already selected. Tapping an item will select it. Tapping another item will change the selection. Tapping the same item again will not unselect it. This means that once an item is selected, one item must remain selected. If you need the ability to unselect all the items in the list, follow these modifications to the original instructions:

  1. Add an MvxCommand to the view model. Call UpdateItemSelections() from the MvxCommand instead of from MyChosenItem.

    public MvxCommand ItemSelectedCommand { get; private set; }
    
    // Constructor
    public ItemSelectionViewModel()
    {
        ItemSelectedCommand = new MvxCommand(OnItemSelected);
    }
    
    public Item MyChosenItem
    {
        get { return _myChosenItem; }
        set
        {
            if (_myChosenItem != value)
            {
                _myChosenItem = value;
                //UpdateItemSelections();    // Move this to OnItemSelected()
                RaisePropertyChanged("MyChosenItem");
            }
        }
    }
    
    private void OnItemSelected()
    {
        UpdateItemSelections();
    }
    
  2. Change UpdateItemSelections() to toggle the isItemSelected property instead of always setting it to true:

    // Select MyChosenItem and unselect all other items
    private void UpdateItemSelections()
    {
        if( MyItems.Count > 0)
        {
            for (int index = 0; index < MyItems.Count; index++)
            {
                // If the chosen item is the same, mark it as selected
                if (MyItems[index].Field1.Equals(MyChosenItem.Field1)
                    && MyItems[index].Field2.Equals(MyChosenItem.Field2))
                {
                    // Toggle selected status
                    MyItems[index].isItemSelected = !MyItems[index].isItemSelected;
                }
                else
                {
                    // Only trigger the property changed event if it needs to change
                    if (MyItems[index].isItemSelected)
                    {
                        MyItems[index].isItemSelected = false;
                    }
                }
            }
        }
    }
    
  3. Remember to check MyChosenItem.isItemSelected == true when saving or doing anything that acts on the selected item in the list. There might be a value in MyChosenItem that is unselected in the list view the user sees.

  4. Bind the MvxCommand to ItemClick in layout definition of the MvxListView:

    <Mvx.MvxListView
        android:layout_width="match_parent"
        android:layout_height="260dp"
        android:layout_marginTop="40dp"
        android:id="@+id/MyMvxListViewControl"
        local:MvxBind="ItemsSource MyItems; SelectedItem MyChosenItem; ItemClick ItemSelectedCommand"
        local:MvxItemTemplate="@layout/my_item_layout" />
    
like image 192
Josh Avatar answered Oct 01 '22 00:10

Josh