'Backwards Shell navigation does not trigger ApplyQueryAttributes and I can't get the query data .NET MAUI
I have a page that is used to select country code. The ListView with country code's is on another page and on selected country code I navigate back to the previous page with the selected country code data and display it there.
The problem is that I am not getting the data from the query route when navigating backwards using the two dots. The debugger won't enter ApplyQueryAttributes at all yet I am brought back to the previous page. I'm using view models and following the documentation as described. Below I will share the code.
There is also a second problem that can be related. And that is when navigating to hte country code selection page from the previous page the button has to be pressed two times. The debugger enters the first row of ApplyQueryAttributes and then exits with no error.
The First page expecting the query routes data ViewModel:
public class OpenAccountViewModel : BaseViewModel, IQueryAttributable
{
public string CountryCode { get; set; }
private string phonenumber;
public string PhoneNumber
{
get => phonenumber;
set
{
phonenumber = value;
OnPropertyChanged(nameof(PhoneNumber));
}
}
private string iconUrl;
public string IconUrl
{
get => iconUrl;
set
{
iconUrl = value;
OnPropertyChanged(nameof(IconUrl));
}
}
public ICommand OpenAccountCommand { get; set; }
public OpenAccountViewModel()
{
IconUrl = "us_icon.png";
OnPropertyChanged(IconUrl);
OpenAccountCommand = new Command(OpenAccount);
}
public void OpenAccount()
{
var fullNumber = $"{CountryCode}{PhoneNumber}";
Shell.Current.GoToAsync($"{nameof(SmsCodePage)}?phone={fullNumber}");
}
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
var countryCode = query["code"].ToString();
var iconUrl = query["icon"].ToString();
if (countryCode is not null && iconUrl is not null)
{
CountryCode = query["code"].ToString();
OnPropertyChanged(CountryCode);
IconUrl = query["icon"].ToString();
OnPropertyChanged(IconUrl);
}
}
}
The Page xaml file
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MobileAppClient.OpenAccountPage"
Title="OpenAccountPage"
xmlns:viewmodels="clr-namespace:MobileAppClient.ViewModels"
x:DataType="viewmodels:OpenAccountViewModel"
Background="{StaticResource BackgroundPageColor}">
<Grid x:Name="MainLayout" Padding="20">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="70"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label
Grid.Row="0"
Text="Open an account"
FontSize="25"
FontAttributes="Bold"
TextColor="White"
Padding="0,0,0,20"
HorizontalOptions="Start"/>
<Label Grid.Row="1"
Text="Sign in with your phone to use SendCrypto's app"
FontSize="17"
FontAttributes="Bold"
TextColor="#6e7191"
Padding="0,0,0,20"
HorizontalOptions="Start"/>
<HorizontalStackLayout Grid.Row="2" Spacing="10">
<Frame HeightRequest="70" BorderColor="#1d1d33" BackgroundColor="#262440">
<HorizontalStackLayout Spacing="15">
<ImageButton x:Name="ImageButton" Clicked="ImageButton_Clicked" Source="{Binding IconUrl}"/>
<Entry Text="{Binding CountryCode}" x:Name="CountryCodeEntry" TextChanged="CountryCodeEntry_TextChanged" HeightRequest="70" TextColor="White" Background="Transparent" Keyboard="Telephone" Placeholder="+1"></Entry>
</HorizontalStackLayout>
</Frame>
<Frame HeightRequest="70" HorizontalOptions="Fill" BorderColor="#1d1d33" BackgroundColor="#262440" WidthRequest="250">
<HorizontalStackLayout>
<Entry Text="{Binding PhoneNumber}" x:Name="PhoneNumberEntry" TextChanged="PhoneNumberEntry_TextChanged" WidthRequest="250" HeightRequest="70" TextColor="White" Background="Transparent" Keyboard="Telephone" Placeholder="Phone Number"></Entry>
</HorizontalStackLayout>
</Frame>
</HorizontalStackLayout>
<Button
x:Name="OpenAccountButton"
Grid.Row="3"
IsEnabled="False"
BackgroundColor="#524695"
Text="Open an account"
TextColor="#888893"
VerticalOptions="End"
Command="{Binding OpenAccountCommand}"/>
</Grid>
</ContentPage>
The second page with the ListView country code selection ViewModel:
public class CountryCodeSelectViewModel : BaseViewModel
{
public ObservableCollection<CountryCode> CountryCodes { get; set; }
private string iconUrl;
public string IconUrl
{
get => iconUrl;
set
{
iconUrl = value;
OnPropertyChanged(nameof(IconUrl));
}
}
private string code;
public string Code
{
get => code;
set
{
code = value;
OnPropertyChanged(nameof(Code));
}
}
public string Country { get; set; }
public string SearchText { get; set; }
public ICommand SearchCommand { get; }
public ICommand SelectedCommand { get; }
private List<CountryCode> countryCodesData = new List<CountryCode>()
{
new CountryCode(){IconUrl = "us_icon.png", Code="+1", Country="USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+2", Country="USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+3", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+4", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+5", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+6", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+7", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+8", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+9", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+10", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+11", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+12", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+13", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+14", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+15", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+16", Country = "USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+17", Country="USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+18", Country="USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+19", Country="USA"},
new CountryCode(){IconUrl = "us_icon.png", Code="+20", Country="USA"},
};
public CountryCodeSelectViewModel()
{
CountryCodes = new ObservableCollection<CountryCode>(countryCodesData);
OnPropertyChanged(nameof(CountryCodes));
SearchCommand = new Command(Search);
SelectedCommand = new Command<CountryCode>(Selected);
}
public void Selected(CountryCode countryCode)
{
Routing.RegisterRoute(nameof(OpenAccountPage), typeof(OpenAccountPage));
Shell.Current.GoToAsync($"..?code={countryCode.Code}");
}
public void Search()
{
Expression<Func<CountryCode, bool>> query = x =>
x.Code.Contains(SearchText) ||
x.Country.Contains(SearchText) ||
x.Code == SearchText ||
x.Country == SearchText;
var newData = countryCodesData.Where(query.Compile()).ToList();
CountryCodes = new ObservableCollection<CountryCode>(newData);
OnPropertyChanged(nameof(CountryCodes));
}
}
The Second page xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MobileAppClient.CountryCodeSelectPage"
Title="CountryCodeSelectPage"
xmlns:viewmodels="clr-namespace:MobileAppClient.ViewModels"
x:DataType="viewmodels:CountryCodeSelectViewModel"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
Background="{StaticResource BackgroundPageColor}">
<ContentPage.Resources>
<ResourceDictionary>
<toolkit:SelectedItemEventArgsConverter x:Key="SelctedItemConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="10" Spacing="30">
<Frame Padding="10,0,0,0" HeightRequest="70" BorderColor="#1d1d33" BackgroundColor="#262440">
<HorizontalStackLayout Spacing="15">
<Image Source="mag_glass_icon.png"/>
<Entry
Text="{Binding SearchText}"
HeightRequest="70"
TextColor="White"
Background="Transparent"
Keyboard="Default"
Placeholder="Search">
<Entry.Behaviors>
<toolkit:EventToCommandBehavior EventName="TextChanged" Command="{Binding SearchCommand}"/>
</Entry.Behaviors>
</Entry>
</HorizontalStackLayout>
</Frame>
<ListView SeparatorVisibility="None" x:Name="CountryCodesList" ItemsSource="{Binding CountryCodes}">
<ListView.Behaviors>
<toolkit:EventToCommandBehavior EventName="ItemSelected" Command="{Binding SelectedCommand}" EventArgsConverter="{StaticResource SelctedItemConverter}"/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<HorizontalStackLayout Spacing="30">
<Image Source="{Binding IconUrl}"/>
<Label VerticalOptions="Center" Text="{Binding Code}"/>
<Label VerticalOptions="Center" Text="{Binding Country}"/>
</HorizontalStackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
How I'm register the routes:
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(TermsAndConditionsPage), typeof(TermsAndConditionsPage));
Routing.RegisterRoute(nameof(OpenAccountPage), typeof(OpenAccountPage));
Routing.RegisterRoute(nameof(CountryCodeSelectPage), typeof(CountryCodeSelectPage));
Routing.RegisterRoute(nameof(SmsCodePage), typeof(SmsCodePage));
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|