10
votes

I am actually working with this piece of code

using System;
using Xamarin.Forms;
using System.Diagnostics;

namespace CryptoUI
{
    public class HomePage : Xamarin.Forms.MasterDetailPage
    {
        public HomePage()
        {
        // Set up the Master, i.e. the Menu
            Label header = new Label
            {
                Text = "MENU",
                Font = Font.SystemFontOfSize(20, FontAttributes.Bold),
                HorizontalOptions = LayoutOptions.Center
            };
        // create an array of the Page names
        string[] myPageNames = {
            "Main",
            "Page 2",
            "Page 3",
        };

        // Create ListView for the Master page.
        ListView listView = new ListView
        {
            ItemsSource = myPageNames,
        };

        // The Master page is actually the Menu page for us
        this.Master = new ContentPage
        {
            Title = "Test",
            Content = new StackLayout
            {
                Children = 
                {
                    header, 
                    listView
                },
            }
        };

        // Define a selected handler for the ListView contained in the Master (ie Menu) Page.
        listView.ItemSelected += (sender, args) =>
        {
            // Set the BindingContext of the detail page.
            this.Detail.BindingContext = args.SelectedItem;

            string currentPage = this.GetType().Name.ToString();

            // This is where you would put your “go to one of the selected pages”
            if(listView.SelectedItem.Equals("Main") && !currentPage.Equals("HomePage")){
                AsyncPush(new HomePage());
            }
            else if(listView.SelectedItem.Equals("Page 2") && !currentPage.Equals("SecondPage")){
                AsyncPush(new SecondPage());
            }
            else if(listView.SelectedItem.Equals("Page 3") && !currentPage.Equals("ThirdPage")){
                AsyncPush(new ThirdPage());
            }               

            // Show the detail page.
            this.IsPresented = false;
        };
            listView.ItemSelected += (senders, e) => {
                if (e.SelectedItem == null) return; // don't do anything if we just de-selected the row
                // do something with e.SelectedItem
                ((ListView)senders).SelectedItem = null; // de-select the row
            };

        // Set up the Detail, i.e the Home or Main page.
        Label myHomeHeader = new Label
        {
            Text = "Home Page",
            HorizontalOptions = LayoutOptions.Center
        };

        string[] homePageItems = { "Alpha", "Beta", "Gamma" };
        ListView myHomeView = new ListView {
            ItemsSource = homePageItems,
        };

        var myHomePage = new ContentPage();

        myHomePage.Content = new StackLayout
        {
            Children = 
            {
                myHomeHeader, 
                myHomeView
            } ,
        };
        this.Detail = myHomePage;
    }

        public async void AsyncPush(Page page)
        {
            await Navigation.PushAsync(page);
        }
    }
}

This code actually shows an easy FlyOut menu, using the Xamarin Forms technologies. I am currently trying to understand how I could easily clear the ListView selection after I have selected which page I want to head to!

I found this piece of code on Xamarin's website for devs (http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/listview/);

listView.ItemSelected += (sender, e) => {
    if (e.SelectedItem == null) return; // don't do anything if we just de-selected the row
    // do something with e.SelectedItem
    ((ListView)sender).SelectedItem = null; // de-select the row
};

But I can't currently figure out how I should integrate it with my code above there :)

5
First you will not need to separate ItemSelected events to do this. I think you can just call this listview.SelectedItem = null to reset the listview selecteditem property. Either before or after you push the new page. Second, it might be better to use ItemTapped to start the page transition and use itemselected to process/pass the data to next view.Jared Reeves

5 Answers

19
votes

I would like to add to Jason's answer because it misses some vital information. When you set the ListView SelectedItem property to null, it will fire off the ItemSelected event again. So if you do not have a null check, it will throw an exception.

This is what it should look like:

void ItemSelected(object sender, EventArgs args)
{
    if (((ListView)sender).SelectedItem == null)
      return;
    //Do stuff here with the SelectedItem ...
    ((ListView)sender).SelectedItem = null;
}
17
votes

You're assigning the ItemSelected handler twice, which is a bad idea. All you should have to do is add this line to your existing ItemSelected handler

  ((ListView)sender).SelectedItem = null; 
4
votes

I had this same problem but the other solutions did not work for me. Since I needed to pass a custom object to the next page I nullified the selected item reference and used the item tapped reference for my custom object.

listView.ItemTapped += async (sender, e) =>{

    await Navigation.PushAsync(new DetailPage(e.Item as CustomObject));
    ((ListView)sender).SelectedItem = null;

};
2
votes

ListView.SelectedItem does not have setter (I mean simple Xamarin Android - not Xamarin.Forms). I suggest to use the following code:

private void DeselectEntities()
{
    if (this.listView != null && this.listView.CheckedItemPositions != null)
    {
        this.listView.CheckedItemPositions.Clear();
    }
}
1
votes

I respect all given answers but in an MVVM app you'd better avoid too much code behind. What I usually do is following:

  1. Bind ItemsSource of ListView as usual to an ObservableCollection where T is a CarViewModel in my case

  2. Set SelectionMode="None": This does avoid the selection of SelectedItem on tap

  3. Use EventToCommandBehavior (I use my own implementation; see github.com or use the one from Prism.Forms) to bind ItemTapped event of ListView to my ViewModel command SelectedCarChangedCommand.

  4. In the ViewModel's SelectedCarChangedCommand you'll receive the tabbed item as ItemTappedEventArgs object.

     <ListView
       x:Name="CarsListView"
       ItemsSource="{Binding Cars}"
       SelectionMode="None">
         <ListView.Behaviors>
             <behaviors:EventToCommandBehavior
                 Command="{Binding SelectedCarChangedCommand}"
                 EventName="ItemTapped" />
         </ListView.Behaviors>