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

.NET MAUI MVVM中基于BottomSheet参数的CollectionView过滤异常

.NET MAUI MVVM CollectionView 过滤问题修正方案

核心问题分析

  1. BottomSheet 绑定上下文缺失:打开BottomSheet时未设置其BindingContext,导致FiltrationCommand无法关联到主页面的ViewModel,过滤逻辑未实际执行
  2. 过滤逻辑缺陷:
    • 直接对Products集合做逐个Add/Remove操作,效率低且易出现集合状态不一致
    • 文本搜索仅单次有效,是因为第一次过滤后Products被修改,后续过滤未基于原始数据源重置
    • NotifyPropertyChanged()未明确指定属性名,UI无法准确感知Products集合的变化
  3. 同步HTTP请求阻塞UI:使用.Result同步获取数据会卡住UI线程,影响用户体验

分步修正方案

1. 修复BottomSheet绑定上下文

在打开BottomSheet的方法中,将其BindingContext设置为当前ViewModel:

private void ToBottom(object obj)
{
    BtmSheet btmSheet = new BtmSheet();
    // 关键:绑定到当前ViewModel
    btmSheet.BindingContext = this;
    btmSheet.ShowAsync();
}

2. 重构过滤逻辑

每次过滤前基于原始数据源Prodyct重置Products,直接赋值过滤后的集合,避免逐个操作元素:

private void OnFiltration(object obj)
{
    var searchM = obj as Manufacturer;
    if (searchM == null) return;
    
    // 基于原始数据过滤后直接赋值,自动触发PropertyChanged
    Products = new ObservableCollection<Product>(
        Prodyct.Where(c => c.manufacturer.Contains(searchM.Title, StringComparison.OrdinalIgnoreCase))
    );
}

private void OnChanged(object obj)
{
    var searchText = obj?.ToString() ?? string.Empty;
    
    // 重置为原始数据后过滤,解决单次有效问题
    Products = new ObservableCollection<Product>(
        Prodyct.Where(contact => contact.Title.Contains(searchText, StringComparison.OrdinalIgnoreCase))
    );
}

3. 替换同步HTTP请求为异步

添加异步初始化方法,避免阻塞UI线程:

public noSfFiltration(INavigation _navigation)
{      
    navigation = _navigation;
    filtrationCommand = new Command<object>(OnFiltration);
    textChanged = new Command<object>(OnChanged);
    bottomMenu = new Command<object>(ToBottom);
    tapCommand = new Command<object>(OnTapped);
    
    // 调用异步初始化方法
    _ = InitializeDataAsync();
}

private async Task InitializeDataAsync()
{
    // 异步获取数据,避免阻塞UI
    Prodyct = await httpClient.GetFromJsonAsync<ObservableCollection<Product>>("http://10.0.2.2:5192/api/Product");
    Products = new ObservableCollection<Product>(Prodyct);

    manufacturers = await httpClient.GetFromJsonAsync<ObservableCollection<Manufacturer>>("http://10.0.2.2:5192/api/Manufacturer");
    Manufacturers = new ObservableCollection<Manufacturer>(manufacturers);
}

4. 修正属性通知逻辑

确保NotifyPropertyChanged正确触发,原代码的CallerMemberName已正确配置,保留即可:

public ObservableCollection<Product> Products
{
    get { return _Products; }
    set
    {
        if (_Products != value)
        {
            _Products = value;
            NotifyPropertyChanged(); // CallerMemberName自动传入属性名"Products"
        }
    }
}

完整修正后的ViewModel代码

public class noSfFiltration : INotifyPropertyChanged
{
    private Command<object> tapCommand;
    private Command<object> textChanged;
    private Command<object> bottomMenu; 
    private Command<object> filtrationCommand;

    public ObservableCollection<Product> Prodyct;
    private ObservableCollection<Product> _Products;

    private ObservableCollection<Manufacturer> _Manufacturers;
    public ObservableCollection<Manufacturer> manufacturers;

    private INavigation navigation;
    static HttpClient httpClient = new HttpClient();
    

    public ObservableCollection<Product> Products
    {
        get { return _Products; }
        set
        {
            if (_Products != value)
            {
                _Products = value;
                NotifyPropertyChanged();
            }
        }
    } 
   
    public ObservableCollection<Manufacturer> Manufacturers
    {
        get { return _Manufacturers; }
        set
        {
            if (_Manufacturers != value)
            {
                _Manufacturers = value;
                NotifyPropertyChanged();
            }
        }
    }


    public Command<object> FiltrationCommand
    {
        get { return filtrationCommand; }
        set { filtrationCommand = value; }
    }
    public Command<object> BottomMenu
    {
        get { return bottomMenu; }
        set { bottomMenu = value; }
    }

    public Command<object> TapCommand
    {
        get { return tapCommand; }
        set { tapCommand = value; }
    }
    public INavigation Navigation
    {
        get { return navigation; }
        set { navigation = value; }
    }
    public Command<object> TextChanged
    {
        get { return textChanged; }
        set { textChanged = value; }
    }
    

    public noSfFiltration(INavigation _navigation)
    {      
        navigation = _navigation;
        filtrationCommand = new Command<object>(OnFiltration);
        textChanged = new Command<object>(OnChanged);
        bottomMenu = new Command<object>(ToBottom);
        tapCommand = new Command<object>(OnTapped);
        
        _ = InitializeDataAsync();
    }

    private async Task InitializeDataAsync()
    {
        Prodyct = await httpClient.GetFromJsonAsync<ObservableCollection<Product>>("http://10.0.2.2:5192/api/Product");
        Products = new ObservableCollection<Product>(Prodyct);

        manufacturers = await httpClient.GetFromJsonAsync<ObservableCollection<Manufacturer>>("http://10.0.2.2:5192/api/Manufacturer");
        Manufacturers = new ObservableCollection<Manufacturer>(manufacturers);
    }

    private void ToBottom(object obj)
    {
        BtmSheet btmSheet = new BtmSheet();
        btmSheet.BindingContext = this;
        btmSheet.ShowAsync();
    }

    private void OnTapped(object obj)
    {
        var newPage = new ProductPage();
        newPage.BindingContext = obj;
        Navigation.PushAsync(newPage);
    }
    
    private void OnFiltration(object obj)
    {
        var searchM = obj as Manufacturer;
        if (searchM == null) return;
        
        Products = new ObservableCollection<Product>(
            Prodyct.Where(c => c.manufacturer.Contains(searchM.Title, StringComparison.OrdinalIgnoreCase))
        );
    }

    private void OnChanged(object obj)
    {
        var searchText = obj?.ToString() ?? string.Empty;
        
        Products = new ObservableCollection<Product>(
            Prodyct.Where(contact => contact.Title.Contains(searchText, StringComparison.OrdinalIgnoreCase))
        );
    }
    

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }   
}

额外优化建议

  • 添加搜索文本为空时重置为全部数据的逻辑:
    private void OnChanged(object obj)
    {
        var searchText = obj?.ToString() ?? string.Empty;
        
        if (string.IsNullOrWhiteSpace(searchText))
        {
            Products = new ObservableCollection<Product>(Prodyct);
            return;
        }
        
        Products = new ObservableCollection<Product>(
            Prodyct.Where(contact => contact.Title.Contains(searchText, StringComparison.OrdinalIgnoreCase))
        );
    }
    
  • 为HttpClient添加生命周期管理,避免重复创建实例

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:42:03