I have a Shell.SearchHandler in use within a tabbed MAUI application (.NET 6). I initiate my application to a specific page where you can search a set of "External Contacts" via an API. I have an additional page to search a set of System Users, again, via an API. When the application initializes. If I navigate to the Users page, it does not update the Search handler, and the search functionality is still using the initial page's template.
Is it possible to modify the template on each page?
ContactsPage.xaml
<Shell.SearchHandler>
<controls:ExternalContactSearchHandler Placeholder="Enter last name"
ShowsResults="true"
ItemTemplate="{StaticResource ExternalContactSearchTemplate}"
ExternalContacts="{x:Static data:ExternalContactData.ExternalContacts}"
SelectedItemNavigationTarget="{x:Type views:ContactDetailPage}" />
</Shell.SearchHandler>
Users.xaml
<Shell.SearchHandler>
<controls:UserSearchHandler Placeholder="Enter User Name"
ShowsResults="true"
ItemTemplate="{StaticResource UserSearchTemplate}"
Users="{x:Static data:UserData.Users}"
SelectedItemNavigationTarget="{x:Type views:UserDetailPage}" />
</Shell.SearchHandler>
App.xaml
<DataTemplate x:Key="UserSearchTemplate">
<Grid Padding="10"
ColumnDefinitions="0.15*,0.85*">
<Image Source="{Binding Images[1].ImageUri}"
HeightRequest="40"
WidthRequest="40" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold"
VerticalOptions="Center" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="ExternalContactSearchTemplate">
<Grid Padding="10" ColumnDefinitions="Auto,Auto,Auto">
<Label Grid.Column="0"
Text="{Binding FirstName}"
FontAttributes="Bold"
HorizontalOptions="Start"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{Binding LastName}"
FontAttributes="Bold"
HorizontalOptions="Start"
VerticalOptions="Center" />
<Label Grid.Column="2"
Text="{Binding Title}"
FontAttributes="Bold"
HorizontalOptions="End"
VerticalOptions="Center" />
</Grid>
</DataTemplate>
CodePudding user response:
This is certainly interesting, since I assumed setting the SearchHandler in each page would work this out, but I moved the Shell.SearchHandler XAML to C# in the respective pages (below), and it still only utilizes the first one used. I am confused. Do I need to rebuild the entire Shell from scratch each time or something? I am not against moving all of this to code vs XAML before rendering, but it seems unnecessary, and just feels like I am missing something.
Users.xaml.cs
public UsersPage()
{
InitializeComponent();
Shell.SetSearchHandler(this, new UserSearchHandler
{
Placeholder = "Enter first or last name",
ShowsResults = true,
SelectedItemNavigationTarget = typeof(UserDetailPage),
Users = UserData.Users,
ItemTemplate = new DataTemplate(() =>
{
Grid grid = new Grid { Padding = 10 };
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.15, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.85, GridUnitType.Star) });
Image image = new Image { HeightRequest = 40, WidthRequest = 40 };
image.SetBinding(Image.SourceProperty, "Images[1].ImageUri");
Label nameLabel = new Label { FontAttributes = FontAttributes.Bold, VerticalOptions = LayoutOptions.Center };
nameLabel.SetBinding(Label.TextProperty, "Name");
grid.Children.Add(image);
grid.Children.Add(nameLabel);
return grid;
})
});
}
ContactsPage.xaml.cs
public ContactsPage()
{
InitializeComponent();
Shell.SetSearchHandler(this, new ExternalContactSearchHandler
{
Placeholder = "Enter search term",
ShowsResults = true,
SelectedItemNavigationTarget = typeof(UserDetailPage),
ExternalContacts = ExternalContactData.ExternalContacts,
ItemTemplate = new DataTemplate(() =>
{
Grid grid = new Grid { Padding = 10 };
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
Label firstNameLabel = new Label { FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Start, VerticalOptions = LayoutOptions.Center };
firstNameLabel.SetBinding(Image.SourceProperty, "FirstName");
Label lastNameLabel = new Label { FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Start, VerticalOptions = LayoutOptions.Center };
lastNameLabel.SetBinding(Label.TextProperty, "LastName");
Label titleLabel = new Label { FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Center };
titleLabel.SetBinding(Label.TextProperty, "Title");
grid.Children.Add(firstNameLabel);
grid.Children.Add(lastNameLabel);
grid.Children.Add(titleLabel);
return grid;
})
});
}
CodePudding user response:
Shell.SetSearchHandler
affects the whole Shell. It is global. When you call it, ALL pages are affected.
Putting it in the constructor is not effective, because the constructor is called at the time Shell is loaded: all the pages get "constructed", even the ones that are not visible yet.
Move the code starting with Shell.SetSearchHandler...
into each page's OnAppearing
method. That runs when that page is about to become visible:
protected override void OnAppearing(...)
{
base.OnAppearing();
Shell.SetSearchHandler...
...
}