Search code examples
c#xamlxamarin.formscollectionview

Scrolling in CollectionView does not maintain Switch toggle selection


I have the following CollectionView -

<CollectionView x:Name="PCollection" ItemsSource="{Binding P.data}" Margin="0,10,2,10">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <StackLayout>
                <Image Source="p" HeightRequest="60" WidthRequest="60" />
                <StackLayout Orientation="Vertical" >
                    <Label Text="{Binding id}" VerticalOptions="Center" IsVisible="False"/>
                    <StackLayout Orientation="Horizontal" >+
                    <Switch IsToggled="{Binding IsOwned, Mode=TwoWay}" HorizontalOptions="Start" Toggled="Switch_Toggled_Place" />
                </StackLayout>
            </StackLayout>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

With my code behind -

bool _isOwned;
public bool IsOwned
{
    get
    {
        return _isOwned;
    }
    set
    {
        _isOwned = value;

    }
}

private void Switch_Toggled_Place(object sender, ToggledEventArgs e)
{

}

My issue is that when I toggle a switch in the collection, everything works as expected and I step into Switch_Toggled_Place. However say there are 20 items in the collection, when I scroll up and the toggled switch goes out of view, for some reason it fires the Switch_Toggled_Place again and unchecks my switch!

I have tried removing the Mode=TwoWay from the binding to no effect. I also tried to establish if the toggle event was happening from a user input or from the code itself firing but again to no effect. How can I resolve this I am sure it is straightforward.


Solution

  • You need to implement the interface INotifyPropertyChanged

    in your model

    public partial class MyModel: INotifyPropertyChanged
    {
       public event PropertyChangedEventHandler PropertyChanged;
    
        void OnPropertyChanged([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    
       bool _isOwned;
       public bool IsOwned
       { 
         get
         {
            return _isOwned;
         }
         set
         {
            if(_isOwned!=value)
                {
                    _isOwned= value;
                    OnPropertyChanged(nameof(IsOwned));
    
                }
    
          }
    }
    
    
    

    And since you had used MVVM , you should handle the logic in ViewModel instead of Event . Otherwise it will have conflict with the data-binding .

    in your ViewMNodel

    public class MyViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        void OnPropertyChanged([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    
        // MySource here is the ItemsSource of CollectionView
        public ObservableCollection<MyModel> MySource { get; set; }
    
    
        public MyViewModel()
        {
            MySource = new ObservableCollection<MyModel>() {
    
           //...
            };
    
            foreach(MyModel model in MySource)
            {
                model.PropertyChanged += Model_PropertyChanged;
            }
    
        }
    
        private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if(e.PropertyName== "IsOwned")
            {
              // do some thing you want here .
            }
        }
    }