XAM280 Using ListView in Xamarin.Forms

Exercise 3: Work with mutable lists (XAM280)

This exercise will continue building the Fun Flags application. You'll add a new Toolbar to the main page of the application and use it to delete flags from the ListView.

To complete the exercise, you will need Visual Studio for Windows or macOS with the Xamarin development tools installed. You will also need either an emulator/simulator or a device to run the exercise on. Please see the setup page if you need help installing the Xamarin development environment.

Open the starter solution

This exercise is a continuation of the previous exercise. You can use your existing solution or begin from the prior Exercise 2 > Completed solution in your copy of the cloned or downloaded course materials.


Add the ToolBarItem elements to the page

Start by adding your ToolBarItem to the page to set your app in "Edit" mode. You'll track the current state and adjust the button text between "Edit" and "Cancel" and two icons indicating edit and cancel.

  1. Open AllFlags.xaml.
  2. Add a new ToolbarItem to the ToolbarItems collection for the page:
    • Give it a meaningful name such as "editButton" so you can access it in code behind
    • Set the Text to "Edit"
    • Set the Icon to "ic_edit.png". This is a pre-supplied image that is already in all the platform-specific projects
    • Set the IsDestructive property to "true"
  3. Wire up an event handler to the Clicked event on your new toolbar item. Name it OnEdit and create the handler in the code behind file. It's defined as a standard EventHandler method.

XML markup

<ContentPage.ToolbarItems><ToolbarItem x:Name="editButton" Text="Edit" Clicked="OnEdit" Icon="ic_edit.png" IsDestructive="true" />
</ContentPage.ToolbarItems>

C# code

public partial class AllFlags : ContentPage
{
    private void OnEdit(object sender, EventArgs e)
    {
    }
}

Change the toolbar button based on editing state

You want to change the button icon and text when you enter and exit "edit" mode. To to this, you could alter the text and icon properties on your existing button, but then you'll have to have duplicate copies of the text and images. Another approach is to use two separate buttons and swap them in and out based on the current editing state. Unfortunately, there's no way to hide a ToolbarItem today so you can't put both of them into the toolbar collection. Instead, you'll store the cancel button in your page resources and then swap them out in your code behind as you enter and exit edit mode.

  1. Add a new ResourceDictionary into the page; remember this needs to be assigned to the ContentPage.Resources property.
  2. Add a ToolbarItem object to the resource dictionary:
    • Give it a key - "cancelEditButton"
    • Set the Text to "Cancel"
    • Set the Icon to "ic_cancel.png" - this is another pre-supplied image that is already in your platform head projects
    • Set the Clicked event to the same handler (OnEdit)
  3. Switch to the code behind file and create a new boolean field named isEditing.
  4. Add a ToolbarItem field named cancelEditButton to the class. In the constructor, after the call to InitializeComponent, set it to your button from Resources. You can get the value like this:

    cancelEditButton = (ToolbarItem)Resources[nameof(cancelEditButton)];
    
  5. In the OnEdit handler, set the isEditing field based on whether the sender argument is the editButton or cancelEditButton. The field should be true if the Edit button was pressed, and false if it was the Cancel button.
  6. Remove the sender ToolbarItem from the ToolbarItems collection and add the correct button based on the state of the isEditing field you just assigned. This should swap the button state.
  7. Try running the application and clicking on your new button; it should switch between the two buttons.

XML markup

<ContentPage.Resources>
    <ResourceDictionary>
        <ToolbarItem x:Key="cancelEditButton" Text="Cancel"
                  Clicked="OnEdit" Icon="ic_cancel.png" />
    </ResourceDictionary>
</ContentPage.Resources>

C# code

public partial class AllFlags : ContentPage
{
   bool isEditing;
   ToolbarItem cancelEditButton;

   public AllFlags()
   {
      ...
      InitializeComponent();

      cancelEditButton = (ToolbarItem)Resources[nameof(cancelEditButton)];
   }
  
   private void OnEdit(object sender, EventArgs e)
   {
      var tbItem = sender as ToolbarItem;
      isEditing = (tbItem == editButton);

      ToolbarItems.Remove(tbItem);
      ToolbarItems.Add(isEditing ? cancelEditButton : editButton);
   }
}

Add support for editing mode

The editing behaviour starts in "normal" mode (the existing behavior), and when you click the new Edit button you'll switch to the "edit" mode. In this mode, the ListView will handle the selection by prompting the user to delete the row. If the user affirms the selection, then the row will be removed from the Flags collection.

  1. Start by disabling the current tap behavior (navigate to details) when you are in edit mode. Find the OnItemTapped method and ignore the tap if you are in edit mode (using the isEditing boolean).

    private async void OnItemTapped(object sender, ItemTappedEventArgs e)
    {
        if (!isEditing)
        {
            await this.Navigation.PushAsync(new FlagDetailsPage());
        }
    }
    
  2. Next, add an event handler for the ListView.ItemSelected event; you can do this in code behind or XAML.

    You could also add this logic into the existing OnItemTapped method, there would be no difference in this case. You are using the ItemSelected event just to demonstrate its use.
  3. In the event handler, check the isEditing field; if it's true, prompt the user with DisplayAlert to delete the selected Flag. You will want to display a "Yes" and "No" selection.
  4. If the user responds "Yes", remove the Flag from the FunFlactsViewModel.Flags collection.
  5. As a final step, when in edit mode, call the OnEdit method to turn off edit mode after an item is deleted. You will need to pass cancelEditButton as the sender.
  6. Run the application and try your change. Does it delete the row? Why or Why not?

    private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        if (isEditing)
        {
            var flag = (Flag)e.SelectedItem;
            if (flag != null &amp;&amp; await this.DisplayAlert("Confirm",
                    $"Are you sure you want to delete {flag.Country}?", "Yes", "No"))
            {
                DependencyService.Get&lt;FunFlactsViewModel>()
                    .Flags.Remove(flag);
            }
            // Reset the edit button
            OnEdit(cancelEditButton, EventArgs.Empty);
        }
    }
    

Use an observable collection

Examine the assignment of the FlagRepository.Flags collection in the FlagData project. Have a look in the constructor. It's currently set to a List<Flag> collection which does not report changes.

Flags = new List<Flag>(flags.OrderBy(f => f.Country));
Notice how the actual property, both here and in our ViewModel, is defined as an IList<T> - this is a good practice to get into because it means you can change the implementation of the underlying list without changing the contract exposed by the class (e.g. the property type).
  1. Instantiate a System.Collections.ObjectModel.ObservableCollection<Flag> instead of the List and then run the app; it should now delete the row and disappear from the UI.

Optional Challenge: fix the selection issue

Tap on an item to navigate to the details page. Then go back and enter Edit mode. Notice that now you cannot tap the currently selected item to delete it! See if you can add the code to fix this minor issue. The completed solution has the fix included.

Hint: there are two ways you can solve this. You can override the OnAppearing to get rid of the selection when you return from the details screen and also de-select the item when you exit edit mode and decide not to delete an item. Alternatively, you can move the logic into the ItemTapped handler which is always called. This is the approach taken by the lab.

Exercise summary

In this exercise, you added support to delete an item from the ListView through a Toolbar button.

You can view the completed solution in the Exercise 3 > Completed folder of your copy of the cloned or downloaded course materials.

Go Back