I have a ListView with an ObservableCollection of Database Entries. When I update the Observable collection the focus of the Entry get lost and the keyboard disappears. Even when i set the focus afterwards manually the keyboard does not appear.
Thats my code.
Page.xaml:
<?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"
xmlns:models="clr-namespace:LoginManager.Models"
x:Class="LoginManager.OverviewPage"
BackgroundColor="{AppThemeBinding Light={StaticResource LightBackground}, Dark={StaticResource DarkBackground}}">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" ColumnSpacing="{OnPlatform WinUI=50, Default=0}" Margin="{OnPlatform WinUI='5,10,10,10', Default='0,-15,-10,-10'}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border StrokeThickness="5"
StrokeShape="RoundRectangle 20"
Padding="0,2.5,0,5"
Style="{StaticResource Border}"
Grid.Column="0"
VerticalOptions="Center">
<Entry x:Name="SearchEntry"
Style="{StaticResource Entry}"
BackgroundColor="Transparent"
FontSize="{OnPlatform WinUI='50', Default='35'}"
Placeholder="Suche"
HorizontalTextAlignment="Center"
VerticalOptions="Center"
Margin="15,0,15,0"
IsEnabled="{Binding IsNotBusy}"
TextChanged="SearchEntry_TextChanged"/>
</Border>
<ImageButton x:Name="SortByBtn"
Grid.Column="1"
Style="{StaticResource ImageButton}"
Source="sort_icon.png"
Aspect="AspectFit"
BackgroundColor="{AppThemeBinding Light={StaticResource LightAccent}, Dark={StaticResource DarkAccent}}"
Margin="0,5,0,5"
Padding="15"
HeightRequest="115"
WidthRequest="115"
HorizontalOptions="End"
VerticalOptions="Center"
IsEnabled="{Binding IsNotBusy}"
Pressed="SortByBtn_Pressed"
Released="SortByBtn_Released"
Clicked="SortByBtn_Clicked"/>
</Grid>
<Border Grid.Row="1"
Style="{StaticResource Border}"
BackgroundColor="{AppThemeBinding Light={StaticResource LightAccent}, Dark={StaticResource DarkAccent}}"
StrokeThickness="2.5"
StrokeShape="RoundRectangle 20"
Padding="2.5"
Margin="{OnPlatform WinUI='0,7.5,0,7.5', Default='0,0,0,7.5'}"/>
<ActivityIndicator Grid.Row="2"
IsRunning="{Binding IsBusy}"
IsVisible="{Binding IsBusy}"
ZIndex="1"
Margin="100"
Color="{AppThemeBinding Light={StaticResource LightPrimary}, Dark={StaticResource DarkPrimary}}"/>
<ListView x:Name="DatabaseEntriesListView"
Grid.Row="2"
Margin="20,0"
BackgroundColor="Transparent"
IsPullToRefreshEnabled="False"
HasUnevenRows="True"
SeparatorVisibility="None"
ItemsSource="{Binding DbEntries}"
HorizontalScrollBarVisibility="Never"
VerticalScrollBarVisibility="Never"
SelectionMode="None"
ItemTapped="DatabaseEntriesListView_ItemTapped">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:DbEntry">
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Bearbeiten"/>
<MenuItem Text="Löschen"/>
</ViewCell.ContextActions>
<Border Style="{StaticResource Border}"
Stroke="{AppThemeBinding Light={StaticResource LightPrimary}, Dark={StaticResource DarkPrimary}}"
BackgroundColor="{AppThemeBinding Light={StaticResource LightSecondary}, Dark={StaticResource DarkSecondary}}"
StrokeThickness="5"
StrokeShape="RoundRectangle 20"
Padding="20"
Margin="{OnPlatform WinUI='0,10,0,10', Default='0,2.5,0,2.5'}">
<Label Style="{StaticResource Label}"
FontSize="{OnPlatform WinUI='35', Default='25'}"
Text="{Binding Name}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
MaxLines="1"
LineBreakMode="NoWrap"/>
</Border>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
Page.xaml.cs:
namespace LoginManager;
using LoginManager.Common;
using LoginManager.ViewModels;
public sealed partial class OverviewPage : ContentPage
{
private readonly OverviewViewModel _vm;
public OverviewPage(OverviewViewModel vm)
{
InitializeComponent();
BindingContext = _vm = vm;
}
protected override async void OnAppearing()
{
base.OnAppearing();
SortByBtn.Scale = 1;
var loadedSuccessfully = await _vm.GetWholeDb(_vm.CurrentUser);
if (!loadedSuccessfully)
{
await DisplayAlert("Fehler", "Die Einträge konnten nicht geladen werden", "Okay");
await Shell.Current.GoToAsync("..", true);
}
}
private async void SearchEntry_TextChanged(object sender, TextChangedEventArgs e)
{
if (!SearchEntry.IsFocused) SearchEntry.Focus();
await _vm.SearchEntriesBy(e.NewTextValue);
if (!SearchEntry.IsFocused)
{
SearchEntry.Focus();
}
}
private async void SortByBtn_Pressed(object sender, EventArgs e)
{
await SortByBtn.ScaleTo(0.95, 100, Easing.Linear);
await SortByBtn.ScaleTo(1, 100, Easing.Linear);
}
private async void SortByBtn_Released(object sender, EventArgs e) => await SortByBtn.ScaleTo(1, 100, Easing.Linear);
private async void SortByBtn_Clicked(object sender, EventArgs e)
{
var chosen = await DisplayActionSheet("Sortieren nach...", null, "Abbrechen",
"neueste zuerst", "älteste zuerst", "Alphabet aufsteigend", "Alphabet absteigend");
var sortBy = chosen switch
{
"neueste zuerst" => SortBy.IdDESC,
"älteste zuerst" => SortBy.IdASC,
"Alphabet aufsteigend" => SortBy.NameASC,
"Alphabet absteigend" => SortBy.NameDESC,
_ => SortBy.None
};
await _vm.SortEntriesBy(sortBy);
}
private void DatabaseEntriesListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
}
}
ViewModel.cs:
namespace LoginManager.ViewModels;
using Common;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LoginManager.Models;
using LoginManager.Services;
using System.Collections.ObjectModel;
[QueryProperty(nameof(CurrentUser), nameof(CurrentUser))]
public sealed partial class OverviewViewModel : ObservableObject
{
private readonly IDatabase _db;
public ObservableCollection<DbEntry> DbEntries { get; set; } = new();
public OverviewViewModel(IDatabase db)
{
_db = db;
}
[ObservableProperty]
User currentUser;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsNotBusy))]
bool isBusy;
public bool IsNotBusy => !IsBusy;
[RelayCommand]
public async Task<bool> GetWholeDb(User currentUser)
{
await LoadDb(currentUser);
return DbEntries.Count is not 0;
}
[RelayCommand]
public Task SortEntriesBy(SortBy sortBy)
{
IsBusy = true;
switch (sortBy)
{
case SortBy.NameASC:
DbEntries.SortBy(x => x.Name);
break;
case SortBy.NameDESC:
DbEntries.SortBy(x => x.Name, false);
break;
case SortBy.IdASC:
DbEntries.SortBy(x => x.Id);
break;
case SortBy.IdDESC:
DbEntries.SortBy(x => x.Id, false);
break;
}
IsBusy = false;
return Task.CompletedTask;
}
[RelayCommand]
public Task SearchEntriesBy(string? query)
{
IsBusy = true;
if (!string.IsNullOrWhiteSpace(query)) DbEntries.SortBy(x => x.Name.Count(x => query.Contains(x)));
IsBusy = false;
return Task.CompletedTask;
}
private async Task LoadDb(User currentUser)
{
IsBusy = true;
await foreach (var entry in _db.GetWholeAsync(currentUser))
{
DbEntries.Add(entry);
}
IsBusy = false;
}
}
I tried setting a Delay for the TextChanged Event but it doesn't work either. Also I don't want the Entry being unfocused and the keyboard going away at all.