UWP AdvancedCollectionView 过滤器不适用于字符串

UWP AdvancedCollectionView filter not working with strings

我有一个 AdvancedCollectionView 来自 Windows Community Toolkit 版本 6.1.1 并尝试使用它来过滤掉 2 个字符串属性。

我创建了一个简单的应用程序来重现该问题:https://github.com/touseefbsb/UWP-Filter-List

它有一个文本框,用于在项目的 StartNumberEndNumber 属性之间进行过滤。

但是当我在其中输入文本“123”时,它在 ListView 中没有显示任何项目,而根据测试逻辑,它实际上应该只显示第一个项目。

代码

MainPage.xaml

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <TextBox
        x:Name="SearchTicketBooksBox"
        Width="300"
        Header="Search"
        TextChanged="SearchTicketBooks_TextChanged" />
    <ListView
        x:Name="TicketBooksListView"
        Grid.Row="1"
        Margin="4,0,0,0"
        ItemsSource="{x:Bind ViewModel.TicketBooks}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="app1:TicketBookDto">
                <StackPanel Margin="20">
                    <TextBlock Text="{x:Bind StartNumber}" />
                    <TextBlock Text="{x:Bind EndNumber}" />
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

MainPage.xaml.cs

 public sealed partial class MainPage : Page
{
    public MainViewModel ViewModel { get; }
    public MainPage() { InitializeComponent(); ViewModel = new MainViewModel(); }

    private void SearchTicketBooks_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (ViewModel != null)
        {
            if (string.IsNullOrWhiteSpace(SearchTicketBooksBox.Text))
            {
                ViewModel.TicketBooks.Filter = _ => true;
            }
            else
            {
                ViewModel.TicketBooks.Filter = x => ((TicketBookDto)x).StartNumber == SearchTicketBooksBox.Text;
                //{
                //    var startNumber = Convert.ToInt32(((TicketBookDto)x).StartNumber);
                //    var endNumber = Convert.ToInt32(((TicketBookDto)x).EndNumber);
                //    var searchText = Convert.ToInt32(SearchTicketBooksBox.Text);
                //    return searchText >= startNumber && searchText <= endNumber;
                //};
            }
        }
    }

    private void Page_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
    {
        ViewModel.TicketBooks.Add(new TicketBookDto { StartNumber = "123", EndNumber = "456" });
        ViewModel.TicketBooks.Add(new TicketBookDto { StartNumber = "789", EndNumber = "987" });
    }
}

MainViewModel

public class MainViewModel : INotifyPropertyChanged
{
    private readonly ObservableCollection<TicketBookDto> _ticketBooksPrivate = new ObservableCollection<TicketBookDto>();
    private AdvancedCollectionView ticketBooks;
    public AdvancedCollectionView TicketBooks
    {
        get
        {
            if (ticketBooks is null)
            {
                ticketBooks = new AdvancedCollectionView(_ticketBooksPrivate, true);
                ticketBooks.ObserveFilterProperty(nameof(TicketBookDto.StartNumber));
                ticketBooks.ObserveFilterProperty(nameof(TicketBookDto.EndNumber));
            }
            return ticketBooks;
        }
        set => Set(ref ticketBooks, value);
    }

    #region INotifyStuff
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return;
        }

        storage = value;
        OnPropertyChanged(propertyName);
    }

    protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    #endregion
}

TicketBookDto

public partial class TicketBookDto
{
    public string StartNumber { get; set; }
    public string EndNumber { get; set; }
}

I have commented out code of the actual logic I need in the filter, which is to show the list items only those who have the search number between their "StartNumber" and "EndNumber" properties. But for simplicity I am using a simple match to StartNumber logic to find out why it aint working. Also when I add a boolean property and simply filter using that property then it works fine, its just not working with these string properties.

恐怕你不能在TextChanged事件中使用Filter,请参考源代码here

if (_filter != null)
        {
            for (var index = 0; index < _view.Count; index++)
            {
                var item = _view.ElementAt(index);
                if (_filter(item))
                {
                    continue;
                }

                RemoveFromView(index, item);
                index--;
            }
        }

当您在 SerchBox 中输入文本时,如果文本不等于过滤器 属性,则该项目将为 RemoveFromView。所以它会让下一个输入不生效。对于这种设计,我们建议您在输入完成后制作提交按钮来过滤数据。

并且如果您确实想在 TextChanged 中过滤 ,您可以在设置 Filter 委托之前调用 ViewModel.TicketBooks.Filter = _ => true; 重置视图方法如下。

private void SearchTicketBooks_TextChanged(object sender, TextChangedEventArgs e)
{

    if (ViewModel != null)
    {
        if (string.IsNullOrWhiteSpace(SearchTicketBooksBox.Text))
        {
            ViewModel.TicketBooks.Filter = _ => true;
        }
        else
        {    // reset the filter
            ViewModel.TicketBooks.Filter = _ => true;
            ViewModel.TicketBooks.Filter = x => ((TicketBookDto)x).EndNumber == SearchTicketBooksBox.Text;

        }
    }
}