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

在.NET MAUI MVVM的ViewModel中实现列表过滤的问题

.NET MAUI MVVM 实现产品列表过滤的正确方式

核心解决思路:将原始产品数据源与UI绑定的过滤后数据源完全分离,避免引用同一集合导致的修改冲突与多次过滤失效,同时规范异步数据加载与集合变更通知逻辑。

1. 修正ViewModel的字段与属性定义

  • 保留只读的原始数据集合,仅在初始化时赋值,后续所有过滤操作均基于该集合执行
  • 使用ObservableCollection<Product>作为UI绑定属性,利用其自带的集合变更通知能力自动更新UI
  • 替换.Result的异步请求方式,改用async/await避免死锁
// 原始完整产品数据:仅初始化时赋值,后续不修改
private List<Product> _originalProducts;

// 用于UI绑定的过滤后数据集合
private ObservableCollection<Product> _filteredProducts;
public ObservableCollection<Product> FilteredProducts
{
    get => _filteredProducts;
    set
    {
        if (_filteredProducts != value)
        {
            _filteredProducts = value;
            OnPropertyChanged();
        }
    }
}

// 假设ViewModel已实现INotifyPropertyChanged接口
public YourViewModel(HttpClient httpClient)
{
    // 异步加载服务器数据
    _ = LoadProductsAsync(httpClient);
}

private async Task LoadProductsAsync(HttpClient httpClient)
{
    _originalProducts = await httpClient.GetFromJsonAsync<List<Product>>("http://10.0.2.2:5192/api/Product");
    // 初始化时显示全部产品
    FilteredProducts = new ObservableCollection<Product>(_originalProducts);
}

2. 实现正确的过滤方法

过滤操作直接基于原始数据生成新结果,避免在枚举集合时修改原集合:

方式一:直接替换ObservableCollection(简单高效)

private void OnFilterTextChanged(string filterText)
{
    if (string.IsNullOrWhiteSpace(filterText))
    {
        // 过滤文本为空时恢复显示全部原始数据
        FilteredProducts = new ObservableCollection<Product>(_originalProducts);
        return;
    }

    // 基于原始数据执行过滤,生成新集合
    var filteredItems = _originalProducts.Where(p => 
        p.Title.Contains(filterText, StringComparison.OrdinalIgnoreCase))
        .ToList();
    FilteredProducts = new ObservableCollection<Product>(filteredItems);
}

方式二:清空现有集合并添加结果(适合需保留集合引用的场景)

如果需要维持FilteredProducts的引用(例如绑定了选择项),可使用该方式:

private void OnFilterTextChanged(string filterText)
{
    FilteredProducts.Clear();
    IEnumerable<Product> targetItems = string.IsNullOrWhiteSpace(filterText) 
        ? _originalProducts 
        : _originalProducts.Where(p => p.Title.Contains(filterText, StringComparison.OrdinalIgnoreCase));
    
    foreach (var product in targetItems)
    {
        FilteredProducts.Add(product);
    }
}

3. 解决原代码的核心问题

  • 枚举修改集合异常:原代码中Products与Prodyct引用同一集合,枚举时修改集合必然触发异常。分离数据源后,枚举的是只读原始集合,修改的是独立的过滤集合,无冲突。
  • 多次过滤失效:原代码修改了原始集合,导致后续过滤无完整数据可用。现在所有过滤均基于未改动的_originalProducts,每次过滤都从完整数据重新计算。
  • UI不更新:ObservableCollection会自动触发集合变更通知,无需手动调用OnPropertyChanged即可更新UI。

4. XAML绑定调整

将UI控件的数据源绑定改为FilteredProducts,示例如下:

<!-- 产品列表绑定 -->
<CollectionView ItemsSource="{Binding FilteredProducts}">
    <!-- 产品模板内容 -->
</CollectionView>

<!-- 过滤输入框绑定(推荐结合ViewModel的FilterText属性) -->
<Entry Text="{Binding FilterText}" />

若需自动触发过滤,可在ViewModel中添加FilterText属性并在setter中调用过滤方法:

private string _filterText;
public string FilterText
{
    get => _filterText;
    set
    {
        if (_filterText != value)
        {
            _filterText = value;
            OnPropertyChanged();
            OnFilterTextChanged(_filterText);
        }
    }
}

内容的提问来源于stack exchange,提问作者vhhs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:30:23