У меня небольшая проблема с множественным выбором и свойством SelectedItem. Мое приложение работает следующим образом: когда я нажимаю на запись в Listview, данные из этой записи отображаются в текстовых полях. И вот моя проблема. Я хочу добиться такой функциональности: когда я нажимаю одну за другой записи, я хочу отобразить данные последнего выбранного элемента. К сожалению, SelectedItem работает только для первого элемента. Не могли бы вы помочь мне? Я приложил необходимую часть кода:
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");
}
}
Я всегда помечаю свой вопрос как сейчас ... Ни у кого нет проблем с этим. Вы первый ... WPF
@ AdrianPrzemysławDrozdowski Вы пробовали использовать SelectedItems? Порядок в этом списке должен соответствовать порядку выбора (за исключением массового выбора, когда они добавляются, как они отображаются)
К сожалению, свойство SelectedItems недоступно в Listview :(
@ AdrianPrzemysławDrozdowski Вы используете собственный ListView? Потому что ListView определенно имеет SelectedItems (см. docs.microsoft.com/en-us/dotnet/api/…)
Нет, это не имеет. Я приложил код своего Listview. Я не делаю ничего особенного со Listview. Когда я набираю SelectedItems в файле xaml, он подчеркивается.
Это потому, что SelectedItems - свойство только для чтения. Что именно вы хотите сделать с последним выбранным элементом?
Сценарий: я нажимаю на одну из записей с удержанием клавиши ctrl, данные отображаются в текстовых полях, я нажимаю на другую запись с удержанием клавиши ctrl, данные этой записи отображаются в текстовых полях, я снова нажимаю на другую запись с удержанием клавиши ctrl, данные этого запись отображается в текстовых полях и т. д.





Я немного поигрался и придумал это решение.
Моей целью было сделать возможным использование 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 :(
@ AdrianPrzemysławDrozdowski Я относительно новичок в шаблоне MVVM, но я думал, что решение будет полностью совместимо с MVVM. Не могли бы вы объяснить, почему это не так?
Я тоже новичок в шаблоне MVVM :) Но наличие MVVM - это отдельная логика от пользовательского интерфейса. К сожалению, вы используете элементы управления View в исходном коде. Вы всегда должны привязывать свои свойства к элементам управления просмотром (их свойствам).
@ AdrianPrzemysławDrozdowski Спасибо за ваше объяснение. После некоторых дополнительных исследований, я думаю, теперь я уловил суть. Я добавил еще одно решение, которое должно быть дружественным к MVVM.
Извините, что долго не отвечал, но мне пришлось прочитать о зависимости, прикрепленных свойствах и поведении :) Теперь я пытаюсь использовать ваше решение в своем приложении.
Я решил свою проблему самостоятельно. Я приложил части добавленного кода. Прежде всего вам нужно сделать шаги из этого статья. Необходимо обрабатывать триггеры взаимодействия. Решение. Когда я выбираю элемент, я добавляю его индекс в список. Свойство 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;
}
}
}
}
На что вы нацеливаетесь: Winforms, WPF, ASP ..? ВЫ должны всегда правильно пометить свои вопросы, чтобы их можно было увидеть на странице вопросов!