Exercise - Manage selection by using a viewmodel
Note
.NET MAUI is the next evolution of Xamarin and what we recommend you develop mobile and desktop apps with, and you can learn more about .NET MAUI in several training modules. This Xamarin training module will not be maintained going forward.
For our exercise, we'll briefly leave behind the example of the human resources app. Instead we'll work with an application that lists famous quotes. The app already has a couple of pages and viewmodels that have some basic bindings. The two pages are a list page and a detail page. We'll modify the selection logic so that the ListView
and the detail page use the same viewmodel property.
Open the starter solution
Open the starter solution from the exercise1 > start folder in your copy of the cloned or downloaded exercise repo in Visual Studio.
Note
If you are planning to run and debug your Xamarin apps on Android from Windows, it is best to clone or download the exercise content to a short folder path, such as C:\dev\, to avoid build-generated files exceeding the maximum path length.
The application displays quotes by using an XML data file as the backing storage. Build and run the app to make sure that it's working before you make changes.
Examine the code
The GreatQuotes project is the common library where the Xamarin.Forms code resides. Take a few minutes to look at the existing solution and see how the viewmodels and pages are structured. If you're familiar with XAML and data-binding in Xamarin.Forms, it should all look familiar. But it's structured by using the MVVM pattern.
There's a MainViewModel
that serves dual duty as the app's overall viewmodel and as the viewmodel for the QuoteListPage
. The other viewmodel is QuoteViewModel
. It also serves dual duty: provide data for the rows in the list page and as the BindingContext
for the QuoteDetailPage
.
This library also contains the rest of the Xamarin.Forms content: the XAML pages, converters, and common data structures that the pass uses.
Add selection support
The first step is to add some property support for selection.
Open the
MainViewModel
and add a new public property of typeQuoteViewModel
that's named SelectedQuote. Use a public getter and setter, and use a backing field.Make sure to implement property-change notification when you set the new property. As a shortcut, you can use the
SetPropertyValue
method from the base view model. This step sets the underlying field and raises a property-change notification. It has the following form:SetPropertyValue<T>(ref T field, T newValue);
QuoteViewModel selectedQuote; public QuoteViewModel SelectedQuote { get { return selectedQuote; } set { SetPropertyValue(ref selectedQuote, value); } }
Next, let's use the property. Open the QuoteListPage.xaml file and locate the
ListView
. Add a new property binding for theSelectedItem
property to the view model property. Use a two-way binding (Mode
property). Leave theItemTapped
event handler in place. You still need that to handle navigation in this simple app. (Most MVVM frameworks have some sort of navigation tool to handle it in the viewmodel, so we'd be able to get rid of that, too.)<ListView ItemsSource="{Binding Quotes}" SelectedItem="{Binding SelectedQuote, Mode=TwoWay}" ItemTapped="OnQuoteSelected">
Next, open the code-behind file (QuoteListPage.xaml.cs), and locate the
OnQuoteSelected
method. It currently passes the tapped item to theQuoteDetailPage
as part of the constructor. Remove the constructor parameter. You don't need it anymore, as long as the second page has access to the view model!Finally, open the QuoteDetailPage.xaml.cs file and fix the constructor to not take the parameter. Instead, change the
BindingContext
to be theSelectedQuote
property of theMainViewModel
. Remember that the view model is a singleton that's exposed by theApp
class.Run the application, and make sure it still works properly. It should correctly navigate and display the quote details on the second screen. But there's a minor but annoying bug on some platforms: When you return to the original screen, the
ListView
still shows the selection, and the cell that you tapped is highlighted. Let's fix that.In the
QuoteDetailPage
constructor, clear theSelectedQuote
property (set it tonull
) after you set theBindingContext
. This clears theListView
selection.Run the app again to verify the change.