Forum Xamarin.Forms

MVVM viewmodel deselect listview item

GaganSinghGaganSingh USUniversity ✭✭✭

Is there a way to deselect a listview item in a viewmodel without having to do it in the xaml code behind?

Answers

  • AdamPAdamP AUUniversity ✭✭✭✭✭

    @GaganSingh - not that I am aware of. However if you remove all selecting tints, you can have a ticked or selected property in each item in the listview, then you can modify any visual selection yourself by changing the item property.

  • ThomasBurkhartThomasBurkhart DEMember ✭✭✭✭

    If you have bound to the SelectedItem Property in your ViewModel you should be able to just set it = null

  • GaganSinghGaganSingh USUniversity ✭✭✭

    The issue is that im navigating on selection. And i need to unselect after navigation. Otherwise im getting null.

  • @ThomasBurkhart said:
    If you have bound to the SelectedItem Property in your ViewModel you should be able to just set it = null

    Thomas, that should work. But I find it doesn't. I have to add this to the view code-behind

    //clear selection
    MyList.ItemSelected += (sender, e) => {
        ((ListView)sender).SelectedItem = null;
    };
    
  • GaganSinghGaganSingh USUniversity ✭✭✭

    @RobertHudson.3621 said:

    @ThomasBurkhart said:
    If you have bound to the SelectedItem Property in your ViewModel you should be able to just set it = null

    Thomas, that should work. But I find it doesn't. I have to add this to the view code-behind

    //clear selection
    MyList.ItemSelected += (sender, e) => {
      ((ListView)sender).SelectedItem = null;
    };
    

    That's what i would like to avoid. I'm trying Telerik's ListView it has a SelecteMode = "None" property.

  • HappyteckHappyteck FRMember

    Hi,

    Did you find a way to do it without telerik ?

  • GaganSinghGaganSingh USUniversity ✭✭✭

    You can attach a tap gesture on the listview item and change the background color on tap. And remove the background color from the other items. Only issue with this is that it loses the touch feedback in android when a tap gesture is applied to a view. Which really sucks.

  • AditkothariAditkothari USMember ✭✭

    @NathanBarrios By default it has mode two way only

  • NMackayNMackay GBInsider, University admin
    edited February 2017

    Couldn't make this in Telerik's RadLIst either via binding, cheated in the end and using messaging to tell the view to clear the Listview's selected items, haven't tried this approach with the vanilla control but you could send a message to the view and do {x:Name}.SelectedItem = null; I guess.

    Simple truth is you can't do everything via binding yet with Forms.

  • hump_truckhump_truck USMember ✭✭

    I use this solution in my ViewModel.

    public FriendCellModel SelectedFriend
    {
      get { return _selectedFriend; }
      set
      {
                  _selectedFriend = value;
    
          var friendProfileViewModel = new FriendProfileViewModel(_selectedFriend);
    
          var friendProfilePage = new FriendsProfilePage();
          friendProfilePage.BindingContext = friendProfileViewModel;
    
          _navigation.PushAsync(friendProfilePage);
    
          OnPropertyChanged(nameof(FriendsList));
       }
     }
    

    You have to implement INotifyPropertyChanged.

    I also have the SelectedItem binding set to TwoWay as mentioned above and the ListView binding to TwoWay as well. Hopefully this will help someone in the future - This thread is the first one found when searching this subject.

  • HistoryaHistorya USMember ✭✭✭

    how to do that with mvvm??

  • DFoulkDFoulk USMember ✭✭✭
    edited August 2017

    @PatrickLynch.Rampros said:
    for some reason with ListView, if you don't put Mode=TwoWay, setting selected item to null in ViewModel code does not clear the selection in UI. So even if TwoWay is the default, there seems to be a difference in behavior with UI.

    Holy crap- this fixed my deselection issues in FreshMvvm! Thank you @PatrickLynch.Rampros!

    Didn't work:

    AircraftListPage.xaml

    <ListView ItemsSource="{Binding GroupedAircraftList}"
              HasUnevenRows="True"
              SelectedItem="{Binding SelectedAircraft}"
              IsGroupingEnabled="True"
              GroupDisplayBinding="{Binding LongName}"
              GroupShortNameBinding="{Binding ShortName}">
    

    AircraftListPageModel.cs

        public IAircraft SelectedAircraft
        {
            get => null;
            set
            {
                CoreMethods.PushPageModel<AircraftPageModel>(value);
                RaisePropertyChanged();
            }
        }
    

    Worked as intended:

    AircraftListPage.xaml

    <ListView ItemsSource="{Binding GroupedAircraftList}"
              HasUnevenRows="True"
              SelectedItem="{Binding SelectedAircraft, Mode=TwoWay}"
              IsGroupingEnabled="True"
              GroupDisplayBinding="{Binding LongName}"
              GroupShortNameBinding="{Binding ShortName}">
    

    AircraftListPageModel.cs

        public IAircraft SelectedAircraft
        {
            get => null;
            set
            {
                CoreMethods.PushPageModel<AircraftPageModel>(value);
                RaisePropertyChanged();
            }
        }
    
  • JulienRosenJulienRosen CAMember ✭✭✭✭

    ` public class ListBoxDeselectSelectedItemBehavior : Behavior
    {
    protected override void OnAttachedTo(ListView bindable)
    {
    base.OnAttachedTo(bindable);

            bindable.ItemSelected += BindableOnItemSelected;
        }
    
        protected override void OnDetachingFrom(ListView bindable)
        {
            bindable.ItemSelected -= BindableOnItemSelected;
    
            base.OnDetachingFrom(bindable);
        }
    
        private void BindableOnItemSelected(object sender, SelectedItemChangedEventArgs selectedItemChangedEventArgs)
        {
            var listView = (ListView) sender;
            listView.SelectedItem = null;
        }
    }`
    
                <ListView.Behaviors>
                    <behavior:ListBoxDeselectSelectedItemBehavior />
                </ListView.Behaviors>
    
  • RaphaelAugustoSilvaRaphaelAugustoSilva USUniversity ✭✭

    Hi

    I've implemented a solution that worked for me:

    1) In you "EventToCommandBehavior.cs" class create a new property (I've named as Source)

    public static readonly BindableProperty SourceProperty = BindableProperty.Create("Source", typeof(object), typeof(EventToCommandBehavior), null);
    
    public object Source
    {
        get { return GetValue(SourceProperty); }
        set { SetValue(SourceProperty, value); }
    }
    

    2) In you EventToCommandBehavior.OnEvent method, in the "if (Converter != null)" part, inform this "Source" property in the "Converter.Convert" third parameter (instead of null)

    void OnEvent(object sender, object eventArgs)
    {
        .....
        ...
        else if (Converter != null)
        {
            resolvedParameter = Converter.Convert(eventArgs, typeof(object), Source, null);
        }
        ...
        .....
    }
    

    3) After that, in your SelectedItemEventArgsToSelectedItemConverter class, on Convert method, implement the trait for this new "parameter". You can do the following:

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        //Clean the ListView.SelectedItem if "Source" property from "EventToCommandBehavior" exists in XAML
        if (parameter != null && parameter is ListView)
            if (((ListView)parameter).SelectedItem != null)
                ((ListView)parameter).SelectedItem = null;
    
        var eventArgs = value as SelectedItemChangedEventArgs;
        return eventArgs.SelectedItem;
    }
    

    And finally, you can use in your XAML like this: (Informing the ListView in Source property)

    /*PS: Add the SelectedItemEventArgsToSelectedItemConverter to your resource dictionary*/
    <ResourceDictionary>
        <converters:SelectedItemEventArgsToSelectedItemConverter x:Key="SelectedItemConverter" />
    </ResourceDictionary>
    ...
    
    <ListView x:Name ="ltvName" ItemsSource="{Binding YourListViewSource}">
        <ListView.Behaviors>
            <b:EventToCommandBehavior Source="{x:Reference ltvName}" EventName="ItemSelected" Command="{Binding YourCommand}" Converter="{StaticResource SelectedItemConverter}" />
        </ListView.Behaviors>
    

    Hope it helps

    Raphael

  • dev.kramdev.kram PHMember ✭✭

    @JulienRosen said:
    ` public class ListBoxDeselectSelectedItemBehavior : Behavior
    {
    protected override void OnAttachedTo(ListView bindable)
    {
    base.OnAttachedTo(bindable);

            bindable.ItemSelected += BindableOnItemSelected;
        }
    
        protected override void OnDetachingFrom(ListView bindable)
        {
            bindable.ItemSelected -= BindableOnItemSelected;
            
            base.OnDetachingFrom(bindable);
        }
    
        private void BindableOnItemSelected(object sender, SelectedItemChangedEventArgs selectedItemChangedEventArgs)
        {
            var listView = (ListView) sender;
            listView.SelectedItem = null;
        }
    }`
    
                <ListView.Behaviors>
                    <behavior:ListBoxDeselectSelectedItemBehavior />
                </ListView.Behaviors>
    

    Indeed. It does work! But I thought the default Mode was TwoWay already?

  • PremrajGillalaPremrajGillala USMember ✭✭

    @DFoulk said:

    @PatrickLynch.Rampros said:
    for some reason with ListView, if you don't put Mode=TwoWay, setting selected item to null in ViewModel code does not clear the selection in UI. So even if TwoWay is the default, there seems to be a difference in behavior with UI.

    Holy crap- this fixed my deselection issues in FreshMvvm! Thank you @PatrickLynch.Rampros!

    Didn't work:

    AircraftListPage.xaml

    <ListView ItemsSource="{Binding GroupedAircraftList}"
              HasUnevenRows="True"
              SelectedItem="{Binding SelectedAircraft}"
              IsGroupingEnabled="True"
              GroupDisplayBinding="{Binding LongName}"
              GroupShortNameBinding="{Binding ShortName}">
    

    AircraftListPageModel.cs

        public IAircraft SelectedAircraft
        {
            get => null;
            set
            {
                CoreMethods.PushPageModel<AircraftPageModel>(value);
                RaisePropertyChanged();
            }
        }
    

    Worked as intended:

    AircraftListPage.xaml

    <ListView ItemsSource="{Binding GroupedAircraftList}"
              HasUnevenRows="True"
              SelectedItem="{Binding SelectedAircraft, Mode=TwoWay}"
              IsGroupingEnabled="True"
              GroupDisplayBinding="{Binding LongName}"
              GroupShortNameBinding="{Binding ShortName}">
    

    AircraftListPageModel.cs

        public IAircraft SelectedAircraft
        {
            get => null;
            set
            {
                CoreMethods.PushPageModel<AircraftPageModel>(value);
                RaisePropertyChanged();
            }
        }
    

    This worked for me.. Thanks.

  • RogerLeblancRogerLeblanc USMember ✭✭
    edited September 2018

    Specifying TwoWay binding mode didn't fix it for me, I ended up creating my own behavior that unselect item with a Command.

    using System.Windows.Input;
    using Xamarin.Forms;
    
    namespace MyProject.Behaviors
    {
        /// <summary>
        /// ListViewBehavior
        /// </summary>
        public class ListViewBehavior : Behavior<ListView>
        {
            /// <summary>
            /// Called when the behavior is attached to the object
            /// </summary>
            /// <param name="bindable"></param>
            protected override void OnAttachedTo(ListView bindable)
            {
                base.OnAttachedTo(bindable);
    
                UnselectItemCommand = new Command(() => bindable.SelectedItem = null);
            }
    
            /// <summary>
            /// Gets or sets the value for UnselectItemCommand. This is a bindable property.
            /// </summary>
            public static readonly BindableProperty UnselectItemCommandProperty =
                BindableProperty.Create(
                    nameof(UnselectItemCommand), 
                    typeof(ICommand), 
                    typeof(ListViewBehavior), 
                    defaultValue: null,
                    defaultBindingMode: BindingMode.OneWayToSource);
    
            /// <summary>
            /// UnselectItemCommand bindable property.
            /// </summary>
            public object UnselectItemCommand
            {
                get { return (object)GetValue(UnselectItemCommandProperty); }
                set { SetValue(UnselectItemCommandProperty, value); }
            }
        }
    }
    
  • @DFoulk said:

    @PatrickLynch.Rampros said:
    for some reason with ListView, if you don't put Mode=TwoWay, setting selected item to null in ViewModel code does not clear the selection in UI. So even if TwoWay is the default, there seems to be a difference in behavior with UI.

    Holy crap- this fixed my deselection issues in FreshMvvm! Thank you @PatrickLynch.Rampros!

    Didn't work:

    AircraftListPage.xaml

    <ListView ItemsSource="{Binding GroupedAircraftList}"
              HasUnevenRows="True"
              SelectedItem="{Binding SelectedAircraft}"
              IsGroupingEnabled="True"
              GroupDisplayBinding="{Binding LongName}"
              GroupShortNameBinding="{Binding ShortName}">
    

    AircraftListPageModel.cs

        public IAircraft SelectedAircraft
        {
            get => null;
            set
            {
                CoreMethods.PushPageModel<AircraftPageModel>(value);
                RaisePropertyChanged();
            }
        }
    

    Worked as intended:

    AircraftListPage.xaml

    <ListView ItemsSource="{Binding GroupedAircraftList}"
              HasUnevenRows="True"
              SelectedItem="{Binding SelectedAircraft, Mode=TwoWay}"
              IsGroupingEnabled="True"
              GroupDisplayBinding="{Binding LongName}"
              GroupShortNameBinding="{Binding ShortName}">
    

    AircraftListPageModel.cs

        public IAircraft SelectedAircraft
        {
            get => null;
            set
            {
                CoreMethods.PushPageModel<AircraftPageModel>(value);
                RaisePropertyChanged();
            }
        }
    

    This worked for me with too, I had to add in a check to make sure the value wasn't null in the set method though

  • gjhdigitalgjhdigital USMember ✭✭

    An old thread I know, but the way I fixed this when using a ListView with a Behavior in MVVM is in the Behavior I wrap it in a try/catch block as the Bindable_ItemSelected() gets called 2x and on the 2nd time around the list.SelectedItem is null and throws "Object not set to instance of an object" error.

    private void Bindable_ItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
    ListView list = sender as ListView;

            try
            {
                object obj = list.SelectedItem.GetType();
                if (obj.ToString().Contains("FAQ"))
                {
                    Models.FAQ f = list.SelectedItem as Models.FAQ;
                    **list.SelectedItem = null;**
                    ViewModels.FAQViewModel m = new ViewModels.FAQViewModel();
                    m.FaqDetailsCommand.Execute(f);                    
                }
            }
            catch { }
        }
    
Sign In or Register to comment.