Как выбрать первый элемент после фильтрации ICollectionView

Я привязываю ListView к ICollectionView в моей модели просмотра. В ICollectionView есть несколько предопределенных фильтров, которые применяются при нажатии некоторых кнопок. Однако я не могу найти способ (автоматически) выбрать первый элемент в ListView после фильтрации коллекции.

Я попытался установить SelectedIndex = 0, добавить в привязку уведомление как Target, так и Source, но все они неэффективны, когда применяется фильтр.

Любые указания о том, как этого добиться?

Обновлено: приведенный ниже код иллюстрирует мою проблему, я бы сказал.

XAML:

<Window x:Class = "CollectionViewTest.MainWindow"
        xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local = "clr-namespace:CollectionViewTest"
        mc:Ignorable = "d"
        Title = "MainWindow" Height = "450" Width = "800">

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <!-- MENU -->
        <StackPanel Orientation = "Vertical">
            <Button Content = "Numbers below 4" Click = "Below4_Click" Width = "100"/>
            <Button Content = "Numbers below 7" Click = "Below7_Click" Width = "100"/>
            <Button Content = "All numbers" Click = "All_Click" Width = "100"/>
        </StackPanel>

        <!-- LIST -->
        <ListView 
            Grid.Column = "1" 
            SelectedIndex = "0"
            ItemsSource = "{Binding Numbers, Mode=OneWay}"
            SelectedItem = "{Binding SelectedNumber, Mode=TwoWay}">
            <ListView.Resources>
                <DataTemplate DataType = "{x:Type local:Number}">
                    <TextBlock Text = "{Binding Value}" />
                </DataTemplate>
            </ListView.Resources>
        </ListView>

        <!-- DETAILS -->
        <TextBlock Grid.Column = "2" Text = "{Binding SelectedNumber.Text}" Width = "100"/>
    </Grid>
</Window>

Код позади:

using System.Windows;

namespace CollectionViewTest
{
    public partial class MainWindow : Window
    {
        private MainViewModel vm;
        public MainWindow()
        {
            InitializeComponent();
            vm = (MainViewModel)DataContext;
        }

        private void Below4_Click(object sender, RoutedEventArgs e)
        {
            vm.MenuFilter = f => f.Value < 4;
        }

        private void Below7_Click(object sender, RoutedEventArgs e)
        {
            vm.MenuFilter = f => f.Value < 7;
        }

        private void All_Click(object sender, RoutedEventArgs e)
        {
            vm.MenuFilter = f => true;
        }
    }
}

ViewModel:

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Data;
using System.Collections.ObjectModel;

namespace CollectionViewTest
{
    public class MainViewModel : PropertyChangedBase
    {

        public MainViewModel()
        {
            Numbers = new ObservableCollection<Number>();
            NumberCollection = CollectionViewSource.GetDefaultView(Numbers);
            NumberCollection.Filter = Filter;
            NumberCollection.SortDescriptions.Add(new SortDescription("Value", ListSortDirection.Ascending));

            for (int i = 0; i < 10; i++)
                Numbers.Add(new Number { Value = i, Text = $"This is number {i}." });

        }

        private Func<Number, bool> menuFilter;
        public Func<Number, bool> MenuFilter
        {
            get => menuFilter;
            set
            {
                menuFilter = value;
                NumberCollection.Refresh();
            }
        }

        private bool Filter(object item)
        {
            var number = (Number)item;
            return MenuFilter == null ? true : MenuFilter(number);
        }

        public ObservableCollection<Number> Numbers { get; set; }
        public ICollectionView NumberCollection { get; set; }

        private Number selectedNumber;
        public Number SelectedNumber { get => selectedNumber; set => Set(ref selectedNumber, value); }
    }

    public class Number : PropertyChangedBase
    {
        public int Value { get; set; }

        private string text;
        public string Text { get => text; set => Set(ref text, value); }
    }

    public class PropertyChangedBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void Set<T>(ref T field, T newValue = default(T), [CallerMemberName] string propertyName = null)
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Как видите, нажатие одной из кнопок изменяет фильтр и вызывает обновление коллекции. Я бы хотел, чтобы первый элемент в списке (здесь «0») выбирался автоматически, что затем отображало бы текст «Это номер 0» в тексте в столбце 2.

Я пробовал как SelectedIndex = 0, так и MoveCurrentToFirst, но ничего не выбрано.

Вы действительно пытались установить для свойства SelectedIndex ListView значение 0? Это должно сработать.

mm8 22.11.2018 13:42

Да и при смене фильтра не работает. Я попытаюсь провести тест вне моего контекста на выходных, но ICollectionView + ListView в моем приложении довольно прост, поэтому я сомневаюсь в этом ... но я вернусь.

Werner 23.11.2018 15:49

@ mm8 Добавлен пример. Кажется, что установка SelectedIndex = 0 работает только в том случае, если я не вызываю ICollectionView.Refresh. Но что нужно для того, чтобы оценить фильтр?

Werner 26.11.2018 09:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
1 405
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Не устанавливайте SelectedIndex при привязке к ICollectionView. Вместо этого установите его CurrentItem через MoveCurrentTo () или MoveCurrentToFirst ():

myCollectionView.MoveCurrentTo(someItem);
...
myCollectionView.MoveCurrentToFirst();

Также установите IsSynchronizedWithCurrentItem на вашем ListView:

<ListView IsSynchronizedWithCurrentItem = "True" ...

Обнаружение применения фильтра

Когда фильтр оценивается, представление коллекции обновляется, что, в свою очередь, сбрасывает коллекцию. Чтобы обнаружить это, прослушайте событие CollectionChanged и найдите флаг NotifyCollectionChangedAction.Reset. Пожалуйста, обратитесь к CollectionViewисходный код для получения более подробной информации.

myCollectionView.MoveCurrentToFirst ()

Rekshino 22.11.2018 15:09

Но MoveCurrentToFirst и производные методы - это то, что мне нужно вызвать, чтобы внести изменения, а не параметр. Afaik, у меня нет возможности узнать, КОГДА выполняется фильтрация?

Werner 23.11.2018 08:41

@ l33t Пожалуйста, посмотрите мой обновленный пример, я пытался подписаться на событие CollectionChanged (хотя не в примере). Но вызов MoveCurrentToFirst () явно ничего не делает.

Werner 26.11.2018 09:37

@ l33t oops - я забыл IsSynchronizedWithCurrentItem, а потом вроде работает! Позвольте мне провести еще несколько тестов ...

Werner 26.11.2018 09:55

@ l33t да, это помогло. LisView нуждается в IsSynchronizedWithCurrentItem. Подпишитесь на CollectionChanged и вызовите MoveCurrentToFirst. Немного запутано, но работает как шарм. Спасибо.

Werner 26.11.2018 10:23

Другие вопросы по теме