NET MAUI:更新ListView时保持Entry的键盘焦点
问题根源
- Entry被禁用导致失焦:你的
SearchEntry绑定了IsEnabled="{Binding IsNotBusy}",执行搜索/排序时IsBusy=true,Entry会被禁用,直接失去焦点;即使后续IsBusy=false恢复启用,焦点也不会自动回归。 - ObservableCollection排序触发全量刷新:你使用的
SortBy扩展方法大概率会触发CollectionChanged的Reset事件,导致ListView完全重新渲染,抢占Entry的焦点。 - 焦点恢复时机不对:直接在
await后调用Focus(),此时UI可能还在处理集合更新的渲染操作,焦点设置不生效。
解决方案
1. 让Entry保持始终可用
修改XAML中SearchEntry的IsEnabled属性,去掉绑定改为固定True,仅保留排序按钮的禁用逻辑:
<!-- 修改SearchEntry的IsEnabled --> <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="True" <!-- 固定为True,确保搜索时可继续输入 --> TextChanged="SearchEntry_TextChanged"/> <!-- SortByBtn保持原IsEnabled绑定,仅在忙碌时禁用 --> <ImageButton x:Name="SortByBtn" ... IsEnabled="{Binding IsNotBusy}" .../>
2. 修改ObservableCollection排序逻辑,避免全量刷新
替换现有SortBy扩展方法,改为逐个移动元素触发Move事件,而非全量重置:
public static class ObservableCollectionExtensions { public static void SortBy<T, TKey>(this ObservableCollection<T> collection, Func<T, TKey> keySelector, bool ascending = true) where TKey : IComparable<TKey> { var sortedItems = ascending ? collection.OrderBy(keySelector).ToList() : collection.OrderByDescending(keySelector).ToList(); for (int i = 0; i < sortedItems.Count; i++) { var targetItem = sortedItems[i]; if (!EqualityComparer<T>.Default.Equals(collection[i], targetItem)) { int currentIndex = collection.IndexOf(targetItem); collection.Move(currentIndex, i); } } } }
这个方法会逐个调整元素位置,ListView只会更新元素顺序,不会完全刷新,避免抢占焦点。
3. 正确时机恢复焦点
修改SearchEntry_TextChanged方法,使用Dispatcher在UI线程完成渲染后再设置焦点:
private async void SearchEntry_TextChanged(object sender, TextChangedEventArgs e) { await _vm.SearchEntriesBy(e.NewTextValue); // 等待UI线程完成集合更新渲染后,再恢复焦点 await Dispatcher.DispatchAsync(() => { if (!SearchEntry.IsFocused) { SearchEntry.Focus(); } }); }
4. 可选:改用CollectionView替代ListView
MAUI中CollectionView性能更好、刷新逻辑更细腻,能减少这类焦点问题。替换XAML中的ListView:
<CollectionView x:Name="DatabaseEntriesCollectionView" Grid.Row="2" Margin="20,0" BackgroundColor="Transparent" ItemsSource="{Binding DbEntries}" SelectionMode="None"> <CollectionView.ItemTemplate> <DataTemplate x:DataType="models:DbEntry"> <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> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView>
验证效果
完成修改后,执行搜索或排序操作时:
- Entry不会被禁用,焦点不会因禁用丢失
- 集合排序仅触发元素移动,ListView局部刷新,不会抢占焦点
- 即使偶尔丢失焦点,Dispatcher调度的Focus操作也能正确恢复
内容的提问来源于stack exchange,提问作者BlyZe
相关产品推荐
相关产品推荐

