Previous part: Horizontal List of Overlapping Images in MAUI [GamesCatalog] - Part 14
Step 1. Let's create the page responsible for the local listing.

Step 1.1. Create the ContentPage GameList.xaml, the class GameListVM.cs, and link both in GameList.xaml.cs.
Code GameListVM.cs
public partial class GameListVM : ViewModelBase
{
}
Code GameList.xaml.cs
public partial class GameList : ContentPage
{
public GameList(GameListVM gameListVM)
{
InitializeComponent();
BindingContext = gameListVM;
}
}
Step 1.2. Add the route.

Code MauiProgram.cs
services.AddTransientWithShellRoute<GameList, GameListVM>(nameof(GameList));
Step 2. In MainVM.cs, we'll create the routes for listing local games.
[RelayCommand]
private Task ListWant() => GoToAsync($"{nameof(GameList)}?GameStatus={(int)GameStatus.Want}");
[RelayCommand]
private Task ListPlaying() => GoToAsync($"{nameof(GameList)}?GameStatus={(int)GameStatus.Playing}");
[RelayCommand]
private Task ListPlayed() => GoToAsync($"{nameof(GameList)}?GameStatus={(int)GameStatus.Played}");
private static Task GoToAsync(string route)
{
return Shell.Current.GoToAsync(route, true);
}
Step 3. In Main.xaml, in the border that represents the card of games with the status "want to play", we'll add the command and a function for the tap gesture.

Step 3.1. In the function created in Main.xaml.cs, we’ll add an animation to represent the tap on the card.
private void TapGestureRecognizer_Tapped(object sender, TappedEventArgs e)
{
var cell = sender as Border;
cell.Opacity = 0.5;
_ = cell.FadeTo(1, 1000);
}
Step 3.2. Repeat with the commands for "playing" and "played":

Code PlayingCard
<Border.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PlayingListCommand}" />
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</Border.GestureRecognizers>
Code PlayedCard
<Border.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PlayedListCommand}" />
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</Border.GestureRecognizers>
Step 3.3. This is how we call the GameList.xaml page.
Step 4. In GameRepo.cs, implement the function that retrieves games by status from the local database and declares them in the interface.

Code
public async Task<List<GameDTO>> GetByStatusAsync(int uid, GameStatus gameStatus, int page)
{
using var context = DbCtx.CreateDbContext();
return await context.Games
.Where(x => x.UserId.Equals(uid) && x.Status == gameStatus && x.Inactive == false)
.OrderByDescending(x => x.UpdatedAt).Skip((page - 1) * pageSize).Take(pageSize)
.ToListAsync();
}
Step 5. In GameService.cs, implement the function that uses GetByStatusAsync and declare it in the interface.
public async Task<List<GameDTO>> GetByStatusAsync(int? uid, GameStatus gameStatus, int page)
{
int _uid = uid ?? 1;
return await GameRepo.GetByStatusAsync(_uid, gameStatus, page);
}
Step 6. Let's create a model to handle the local game listing, which inherits the fields from UIIGDBGame.
namespace GamesCatalog.Models
{
public class UIGame : UIIGDBGame
{
public int LocalId { get; set; }
public GameStatus Status { get; set; }
public int? Rate { get; set; }
public bool RateIsVisible => Rate > 0;
}
}
Step 7. In GameListVM.cs, it will be very similar to the API-based game listing.

It uses the IGameService and reads parameters passed via query.
public partial class GameListVM(IGameService gameService) : ViewModelBase, IQueryAttributable
It has a list that is passed to the front end.
private ObservableCollection<UIGame> games = [];
public ObservableCollection<UIGame> Games
{
get => games;
set => SetProperty(ref games, value);
}

Reads the status passed by the query, sets the page title, and loads the first page of the list.
private GameStatus GameStatus { get; set; }
private string titleStatus = "";
public string TitleStatus
{
get => titleStatus;
set => SetProperty(ref titleStatus, value);
}
private int CurrentPage { get; set; }
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
if (query != null && query.TryGetValue("GameStatus", out object? outValue))
{
if (outValue is null) throw new ArgumentNullException("gameStatus");
GameStatus = (GameStatus)Convert.ToInt32(outValue);
TitleStatus = GameStatus switch
{
GameStatus.Want => "Want to play",
GameStatus.Playing => "Playing",
GameStatus.Played => "Played",
_ => throw new ArgumentOutOfRangeException("gameStatus"),
};
}
else throw new ArgumentNullException(nameof(query));
CurrentPage = 1;
_ = LoadGames();
}






Join the conversation! Your thoughts help the community grow.