ScumSprocket
ScumSprocket

Reputation: 440

What is up with capturing Xamarin CollectionView Selections only sending null objects?

I have been using every example on the internet to make my CollectionView send the object to my ViewModel, with no luck! This is being tested as an Android project, by the way.

One weird thing to note: instead of the selected cells turning orange (like the MS sample solutions), my CollectionView items just flash grey, real quick. I haven't applied any formatting, so I don't know why that would be any different than the default.

I tried to be thorough but brief, to understand what's not working. I have used the term "My" to represent my custom class which I believe you have to cast CollectionView's output into.

My Xaml...

<CollectionView                 
  x:Name="MyCollectionView"
  SelectionMode="Multiple"  //Why don't my cells remain highlighted?
  ItemsSource="{Binding MyItemSource}"
  SelectedItem="{Binding SelectedItem, Mode=TwoWay}"    //This doesn't seem to do anything
  SelectedItems="{Binding SelectedItems, Mode=TwoWay}"  //This doesn't seem to do anything
  SelectionChangedCommand="{Binding SelectionChangedCommand}">  //This gets called on tapping
    <CollectionView.ItemTemplate> <!--Contains a Grid with labels and an icon-->

My View Model...

  private ObservableCollection<MyClass> _myItemSource;
  public ObservableCollection<MyClass> MyItemSource {get... set... data from database}
   
  //Isn't this the selected items?  I think I have to cast this list's objects to my class.
  public IList<object> ItemsSelected { get; set; }  

  //To cast objects returned from CollectionView into my class:
  public ObservableCollection<MyClass> MyItemsSelected { get; set; }  


  //Shouldn't SelectedItem be the most recently tapped item?  
  private MyClass _selectedItem;  
  public MyClass SelectedItem 
  
  {
    get
    {
        return _selectedItem;
        //Should this be : return (MyClass)_selectedItem?
    }
    set
    {
        _selectedItem = value;              
        OnPropertyChanged();
    }
  }

//No problem calling this, but it's arg is always null:
  public ICommand SelectionChangedCommand { get; set; }

//My Constructor...
  ItemsSelected = new List<object>();
  MyItemsSelected = new ObservableCollection<MyClass>();
  SelectionChangedCommand = new Command<object>(OnSelectionChangedCommand); //object is always null!

//My One Method... gets called dependably, but obj is always null!
    private void OnSelectionChangedCommand(object obj) 
    {
        //if (obj != null)
        {
            var x = (MyClass)obj;

            if (MyItemsSelected .Contains(x))
            {
                MyItemsSelected .Remove(x);
            }
            else
            {
                MyItemsSelected .Add(x);
            }
        }
        Debug.WriteLine("~~~ OnSelectionChangedCommand Called");
    }

I think I'm following a really basic MVVM pattern, but any functioning example doesn't seem to fit my project... I just want to grab the item selected... I'd prefer multiple selections that remain highlighted until re-tapped (like the MS Docs example), but I'm not picky. I'd really like to understand what is going on, here. Thanks!

Upvotes: 1

Views: 2129

Answers (2)

ScumSprocket
ScumSprocket

Reputation: 440

Problem was actually in the Xaml! It appears < Frame > was causing an issue with the CollectionView's selection. I scrapped my DataTemplate, and backend worked. After... a lot of hours tweaking the wrong parts, I can say I learned a lot about Selections, though!
This link, provided by Jason, was probably the most thorough reference I have read for actually implementing the selections, and bug you may face: https://github.com/xamarin/Xamarin.Forms/issues/6891#issuecomment-511936153

Update in case you hit this, too: "Frame does not have a selected state" More discussion: https://forums.xamarin.com/discussion/174791/using-the-visualstatemanager-in-a-collectionviews-itemtemplate

Thanks for your help and encouragement.

Upvotes: 2

Jarvan Zhang
Jarvan Zhang

Reputation: 1013

One weird thing to note: instead of the selected cells turning orange (like the MS sample solutions), my CollectionView items just flash grey, real quick.

Which platform did you test on? The color of the selected items is orange on Android, it will be gray on iOS.

I tested a basic demo about the function and it works fine. Here is the sample link, you could refer to it.

public class CustomViewModel : INotifyPropertyChanged
{
    readonly IList<CustomModel> source;
    public ObservableCollection<CustomModel> DataCollection { get; private set; }

    ObservableCollection<object> theSelectedItems;
    public ObservableCollection<object> TheSelectedItems
    {
        get
        {
            return theSelectedItems;
        }
        set
        {
            if (theSelectedItems != value)
            {
                theSelectedItems = value;
            }
        }
    }

    public CustomViewModel()
    {
        source = new List<CustomModel>();

        //add the items

        DataCollection = new ObservableCollection<CustomModel>(source);

        TheSelectedItems = new ObservableCollection<object>() { DataCollection[1], DataCollection[3], DataCollection[4] };
    }

    protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Related link: https://github.com/xamarin/xamarin-forms-samples/blob/master/UserInterface/CollectionViewDemos/CollectionViewDemos/Views/Selection/VerticalListMultiplePreSelectionPage.xaml.cs

Update:

I tested the code with Frame and face the same issue, the selected item will not highlight. This may be a potential issue, you could report it to the product team on github.

There is a workaround for this issue, try using VisualStateManager to add the selected effect.

<DataTemplate>
    <Frame>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Selected">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="DarkOrange" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Normal">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="White" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <StackLayout>
            ...
        </StackLayout>
    </Frame>
</DataTemplate>

Upvotes: 0

Related Questions