Как получить последний элемент из Listview, когда элементы выделены множественным выбором

У меня небольшая проблема с множественным выбором и свойством SelectedItem. Мое приложение работает следующим образом: когда я нажимаю на запись в Listview, данные из этой записи отображаются в текстовых полях. И вот моя проблема. Я хочу добиться такой функциональности: когда я нажимаю одну за другой записи, я хочу отобразить данные последнего выбранного элемента. К сожалению, SelectedItem работает только для первого элемента. Не могли бы вы помочь мне? Я приложил необходимую часть кода:

Как получить последний элемент из Listview, когда элементы выделены множественным выбором

MainWindow.xaml

<ListView Name = "EmployeeListView" HorizontalAlignment = "Left" Height = "160" Margin = "0,153,0,0" VerticalAlignment = "Top" Width = "755" Grid.Row = "1" ItemsSource = "{Binding FilteredCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem = "{Binding SelectedEmployee, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedIndex = "{Binding SelectedIndex}" IsSynchronizedWithCurrentItem = "True">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header = "EmployeeName" Width = "150" DisplayMemberBinding = "{Binding EmployeeName}" />
                    <GridViewColumn Header = "EmployeeID" Width = "150" DisplayMemberBinding = "{Binding EmployeeID}" />
                    <GridViewColumn Header = "EmployeeSalary" Width = "150" DisplayMemberBinding = "{Binding EmployeeSalary}" />
                    <GridViewColumn Header = "EmployeeDesigner" Width = "150" DisplayMemberBinding = "{Binding EmployeeDesigner}" />
                    <GridViewColumn Header = "EmployeeEmailID" Width = "150" DisplayMemberBinding = "{Binding EmployeeEmailID}" />
                </GridView>
            </ListView.View>
            <ListView.ItemContainerStyle>
                <Style TargetType = "{x:Type ListViewItem}">
                    <Setter Property = "IsSelected" Value = "{Binding Mode=TwoWay, Path=IsSelected, UpdateSourceTrigger=PropertyChanged}"/>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>

Employee.cs

            public IEnumerable<Employee> SelectedEmployees
            {
                get
                {
                    selectedEmployees = Employees.Where(o => o.IsSelected).ToList();
                    return selectedEmployees;
                }
                set
                {
                    selectedEmployees = value;
                    OnPropertyChanged("SelectedEmployees");
                }
            }

            public bool IsSelected
            {
                get
                {
                    //Application.Current.Dispatcher.BeginInvoke(new Action(() => MessageBox.Show("SELE")));

                    return isSelected;
                }
                set
                {
                    isSelected = value;
                    OnPropertyChanged("IsSelected");
                }
            }

            public ObservableCollection<Employee> Employees
            {
                get
                {
                    return employees;
                }
                set
                {
                    employees = value;
                    OnPropertyChanged("Employees");
                }
            }

            public ICollectionView FilteredCollection
            {
                get
                {
                    return filteredCollection;
                }
                set
                {
                filteredCollection = value;
                OnPropertyChanged("FilteredCollection");
                }
            }


            public Employee SelectedEmployee
            {
                get
                {
                    //Application.Current.Dispatcher.BeginInvoke(new Action(() => MessageBox.Show(selectedEmployee.SelectedEmployee.ToString())));
                    return selectedEmployee;
                }
                set
                {
                    selectedEmployee = value;

                    if (selectedEmployee == null)
                    {
                        ModeOfExecuting = "Searching / Adding Mode";
                        OnPropertyChanged("SelectedEmployee");
                        OnPropertyChanged("ModeOfExecuting");
                        OnPropertyChanged("FilteredCollection");
                    }

                    if (selectedEmployee != null)
                    {
                        //MessageBox.Show(Employees[SelectedIndex.GetValueOrDefault()].EmployeeName.ToString());

                        //List<Employee> FilteredCollectionList = FilteredCollection.Cast<Employee>().ToList();
                        //MessageBox.Show(FilteredCollectionList[0].EmployeeName);
                        if (selectedEmployee.EmployeeName != string.Empty)
                        {
                            RememberValueEmployeeName = selectedEmployee.EmployeeName;
                            DynamicSearchEmployeeName = RememberValueEmployeeName;
                        }
                        if (selectedEmployee.EmployeeID != null)
                        {
                            RememberValueEmployeeID = selectedEmployee.EmployeeID;
                            DynamicSearchEmployeeID = RememberValueEmployeeID;
                        }
                        if (selectedEmployee.EmployeeSalary != null)
                        {
                            RememberValueEmployeeSalary = selectedEmployee.EmployeeSalary;
                            DynamicSearchEmployeeSalary = RememberValueEmployeeSalary.ToString();
                        }
                        if (selectedEmployee.EmployeeDesigner != string.Empty)
                        {
                            RememberValueEmployeeDesigner = selectedEmployee.EmployeeDesigner;
                            DynamicSearchEmployeeDesigner = RememberValueEmployeeDesigner;
                        }
                        if (selectedEmployee.EmployeeEmailID != string.Empty)
                        {
                            RememberValueEmployeeEmailID = selectedEmployee.EmployeeEmailID;
                            DynamicSearchEmployeeEmailID = RememberValueEmployeeEmailID;
                        }

                        ModeOfExecuting = "Editing Mode";

                        OnPropertyChanged("SelectedEmployee");

                    }

                    OnPropertyChanged("SelectedEmployee");
                }
            }

На что вы нацеливаетесь: Winforms, WPF, ASP ..? ВЫ должны всегда правильно пометить свои вопросы, чтобы их можно было увидеть на странице вопросов!

TaW 03.12.2018 13:11

Я всегда помечаю свой вопрос как сейчас ... Ни у кого нет проблем с этим. Вы первый ... WPF

Adrian Przemysław Drozdowski 03.12.2018 13:22

@ AdrianPrzemysławDrozdowski Вы пробовали использовать SelectedItems? Порядок в этом списке должен соответствовать порядку выбора (за исключением массового выбора, когда они добавляются, как они отображаются)

nosale 03.12.2018 14:28

К сожалению, свойство SelectedItems недоступно в Listview :(

Adrian Przemysław Drozdowski 03.12.2018 14:54

@ AdrianPrzemysławDrozdowski Вы используете собственный ListView? Потому что ListView определенно имеет SelectedItems (см. docs.microsoft.com/en-us/dotnet/api/…)

nosale 03.12.2018 15:09

Нет, это не имеет. Я приложил код своего Listview. Я не делаю ничего особенного со Listview. Когда я набираю SelectedItems в файле xaml, он подчеркивается.

Adrian Przemysław Drozdowski 03.12.2018 15:29

Это потому, что SelectedItems - свойство только для чтения. Что именно вы хотите сделать с последним выбранным элементом?

mm8 03.12.2018 16:42

Сценарий: я нажимаю на одну из записей с удержанием клавиши ctrl, данные отображаются в текстовых полях, я нажимаю на другую запись с удержанием клавиши ctrl, данные этой записи отображаются в текстовых полях, я снова нажимаю на другую запись с удержанием клавиши ctrl, данные этого запись отображается в текстовых полях и т. д.

Adrian Przemysław Drozdowski 03.12.2018 18:03
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
8
493
2

Ответы 2

Я немного поигрался и придумал это решение. Моей целью было сделать возможным использование Binding. Если вы обнаружите какую-либо ошибку, дайте мне знать.

Я создал прикрепленное поведение и соответствующее прикрепленное свойство

public static class LastSelectedItemBehavior
{
    public static readonly DependencyProperty ExposeLastSelectedItemProperty =
        DependencyProperty.RegisterAttached("ExposeLastSelectedItem", typeof(bool), typeof(LastSelectedItemBehavior),
            new PropertyMetadata(false, ExposeLastSelectedItemChanged));
    public static bool GetExposeLastSelectedItem(ListBox element)
    {
        return (bool)element.GetValue(ExposeLastSelectedItemProperty);
    }
    public static void SetExposeLastSelectedItem(ListBox element, bool value)
    {
        element.SetValue(ExposeLastSelectedItemProperty, value);
    }
    private static void ExposeLastSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is ListBox listBox && e.NewValue is bool boolValue)
        {
            if (boolValue)
            {
                listBox.SelectionChanged += ListBox_SelectedItemsChanged;
            }
            else
            {
                listBox.SelectionChanged -= ListBox_SelectedItemsChanged;
            }
        }
    }
    private static void ListBox_SelectedItemsChanged(object sender, SelectionChangedEventArgs e)
    {
        var listBox = (ListBox) sender;
        listBox.SetValue(LastSelectedItemPropertyKey, listBox.SelectedItems.Count > 0 ? listBox.SelectedItems[listBox.SelectedItems.Count - 1] : default(object));
    }

    private static readonly DependencyPropertyKey LastSelectedItemPropertyKey =
        DependencyProperty.RegisterAttachedReadOnly(
            "LastSelectedItem", 
            typeof(object), 
            typeof(LastSelectedItemBehavior),
            new PropertyMetadata(default(object)));
    public static readonly DependencyProperty LastSelectedItemProperty = LastSelectedItemPropertyKey.DependencyProperty;

    public static object GetLastSelectedItem(ListBox element)
    {
        return element.GetValue(LastSelectedItemProperty);
    }
}

На вашем ListView вы должны установить ExposeLastSelectedItem

<ListView x:Name = "listView" local:LastSelectedItemBehavior.ExposeLastSelectedItem = "True" />

А потом можно привязать вот так

<ContentControl Content = "{Binding ElementName=listView, Path=(local:LastSelectedItemBehavior.LastSelectedItem)}" />

Редактировать:

Дружественное решение MVVM с использованием метода из Дмитрий Ташкинов

<ListView x:Name = "listView" local:LastSelectedItemBehavior.ExposeLastSelectedItem = "True">
    <local:DataPiping.DataPipes>
        <local:DataPipeCollection>
            <local:DataPipe 
                Source = "{Binding RelativeSource = {RelativeSource AncestorType = {x:Type ListView}}, Path=(local:LastSelectedItemBehavior.LastSelectedItem)}"
                Target = "{Binding Path=LastSelectedItem, Mode=OneWayToSource}"/>
        </local:DataPipeCollection>
    </local:DataPiping.DataPipes>
</ListView>

Я очень благодарен за ваше решение, но оно написано не на чистой архитектуре MVVM :(

Adrian Przemysław Drozdowski 04.12.2018 09:46

@ AdrianPrzemysławDrozdowski Я относительно новичок в шаблоне MVVM, но я думал, что решение будет полностью совместимо с MVVM. Не могли бы вы объяснить, почему это не так?

nosale 04.12.2018 11:11

Я тоже новичок в шаблоне MVVM :) Но наличие MVVM - это отдельная логика от пользовательского интерфейса. К сожалению, вы используете элементы управления View в исходном коде. Вы всегда должны привязывать свои свойства к элементам управления просмотром (их свойствам).

Adrian Przemysław Drozdowski 04.12.2018 12:54

@ AdrianPrzemysławDrozdowski Спасибо за ваше объяснение. После некоторых дополнительных исследований, я думаю, теперь я уловил суть. Я добавил еще одно решение, которое должно быть дружественным к MVVM.

nosale 04.12.2018 18:27

Извините, что долго не отвечал, но мне пришлось прочитать о зависимости, прикрепленных свойствах и поведении :) Теперь я пытаюсь использовать ваше решение в своем приложении.

Adrian Przemysław Drozdowski 06.12.2018 09:59

Я решил свою проблему самостоятельно. Я приложил части добавленного кода. Прежде всего вам нужно сделать шаги из этого статья. Необходимо обрабатывать триггеры взаимодействия. Решение. Когда я выбираю элемент, я добавляю его индекс в список. Свойство SelectedItem установлено в выбранном мной элементе. Затем свойство IsSelected устанавливается в значение true для этих сотрудников, индексы которых находятся в списке. Я использую HashSet, чтобы избежать дублирования значений: когда я выбрал второй элемент в списке, индекс первого выбранного элемента был добавлен в список еще раз. Не стесняйтесь комментировать :)

XAML-файл (добавить в элемент управления ListView):

            <i:Interaction.Triggers>
                <i:EventTrigger EventName = "SelectionChanged">
                    <i:InvokeCommandAction Command = "{Binding SelectionChanged}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>

Файл Employee.cs

        public HashSet<int> IndexesOfSelectedEmployees
        {
            get
            {
                return indexesOfSelectedEmployees;
            }
            set
            {
                indexesOfSelectedEmployees = value;
                OnPropertyChanged("IndexesOfSelectedEmployees");
            }
        }

Файл EmployeeViewModel.cs:

          public void OnSelectionChanged(object parameter)
          {

            if (SelectedEmployee == null)
            {
                IndexesOfSelectedEmployees.Clear();
            }
            if (SelectedEmployee != null)
            {
                foreach (Employee item in Employees)
                {
                    if (item.IsSelected == true)
                    {
                        IndexesOfSelectedEmployees.Add(SelectedIndex.GetValueOrDefault());
                    }
                }
                foreach (int itemIndexesOfSelectedEmployees in IndexesOfSelectedEmployees)
                {
                    foreach (Employee itemEmployees in Employees)
                    {
                        if (itemIndexesOfSelectedEmployees == Employees.IndexOf(itemEmployees))
                        {
                            itemEmployees.IsSelected = true;
                        }
                    }
                }
          }

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