You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NET MAUI:更新ListView时保持Entry的键盘焦点

问题根源

  1. Entry被禁用导致失焦:你的SearchEntry绑定了IsEnabled="{Binding IsNotBusy}",执行搜索/排序时IsBusy=true,Entry会被禁用,直接失去焦点;即使后续IsBusy=false恢复启用,焦点也不会自动回归。
  2. ObservableCollection排序触发全量刷新:你使用的SortBy扩展方法大概率会触发CollectionChanged的Reset事件,导致ListView完全重新渲染,抢占Entry的焦点。
  3. 焦点恢复时机不对:直接在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 16:54:58