Получение WPF ListView.SelectedItems в ViewModel
Есть несколько сообщений, посвященных добавлению возможности привязки данных для ListView.SelectedItems
с нетривиальным количеством кода. В моем сценарии мне не нужно устанавливать его из ViewModel
, просто получая выбранные элементы для выполнения действий над ними, и он запускается командой, поэтому обновление push также не требуется.
Есть ли простое решение (в терминах строк кода), возможно, в коде? Я в порядке с кодом, пока View
и ViewModel
не должны ссылаться друг на друга. Я думаю, что это более общий вопрос: "Лучшая практика для VM для получения данных из представления по требованию", но я не могу найти ничего...
Ответы
Ответ 1
Чтобы получить SelectedItems
только при выполнении команды, используйте CommandParameter
и перейдите в ListView.SelectedItems
.
<ListBox x:Name="listbox" ItemsSource="{Binding StringList}" SelectionMode="Multiple"/>
<Button Command="{Binding GetListItemsCommand}" CommandParameter="{Binding SelectedItems, ElementName=listbox}" Content="GetSelectedListBoxItems"/>
Ответ 2
Это может быть достигнуто с помощью триггеров взаимодействия, как показано ниже
Добавьте ниже xmlns в свой xaml
xmlns:i="http://schemas.microsoft.com/expression//2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Добавьте код ниже только в свой тег GridView
<GridView x:Name="GridName">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding Datacontext.SelectionChangedCommand, ElementName=YourUserControlName}" CommandParameter="{Binding SelectedItems, ElementName=GridName}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Код Inside ViewModel объявляет свойство ниже
public DelegateCommand<object> SelectionChangedCommand {get;set;}
внутри конструктора Viewmodel инициализирует команду ниже
SelectionChangedCommand = new DelegateCommand<object> (items => {
var itemList = (items as ObservableCollection<object>).Cast<YourDto>().ToList();
}
Ответ 3
Я не думаю, что это правильное условие считать, что View и ViewModel не должны знать друг друга; В представлении MVVM всегда знают о ViewModel.
Я также сталкивался с такой ситуацией, когда мне приходилось обращаться к ViewModel в виде кода, а затем заполнять некоторые данные (например, выбранные элементы), это становится необходимым при использовании 3-х сторонних элементов управления, таких как ListView, DataGrid и т.д.
Если прямое связывание свойства VM невозможно, я бы прослушал событие ListViw.SelectionChanged, а затем обновил свойство ViewModels SelectedItems в этом случае.
Update:
Чтобы включить вывод данных из ВМ, вы можете открыть интерфейс в представлении, который обрабатывает специфичные для просмотра функции, а ViewModel будет ссылаться на ваш вид через этот интерфейс; Использование интерфейса по-прежнему удерживает View и ViewModel в значительной степени развязанным, но я в общем не предпочитаю этого.
MVVM, предоставляя ассоциацию View to ViewModel
Я бы предпочел бы одобрить обработку события в представлении и сохранить обновленную виртуальную машину (с выбранными элементами), таким образом, VM не нужно беспокоиться о том, чтобы вытащить данные перед выполнением какой-либо операции, просто нужно использовать доступные данные (поскольку это всегда будет обновлено).
Ответ 4
Я могу заверить вас: SelectedItems действительно может быть связан как XAML CommandParameter
После большого поиска и поиска в Google я наконец нашел простое решение этой общей проблемы.
Чтобы заставить его работать, вы должны следовать ВСЕМ следующим правилам:
-
Следуя Ed Ball suggestion ', вы привязываетесь к базе данных XAML, определите свойство CommandParameter ПЕРЕД Командой свойство. Это очень трудоемкая ошибка.
-
Убедитесь, что методы ICommand CanExecute и Execute имеют параметр object. Таким образом, вы можете предотвратить исключение из-за исключения, которое возникает, когда тип привязки CommandParameter не соответствует типу параметра метода команды.
private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)
{
// Your goes heres
}
private bool OnDeleteSelectedItemsExecute(object SelectedItems)
{
// Your goes heres
}
Например, вы можете отправить свойство listview/listbox SelectedItems для вас ICommand или listview/listbox. Отлично, не так ли?
Надеюсь, что это не позволяет кому-то потратить огромное количество времени, которое я сделал, чтобы выяснить, как получить SelectedItems как параметр CanExecute.
Ответ 5
Так как ни один из ответов не помог мне (используя SelectedItems
как CommandParameter
всегда null
), вот решение для приложений Universal Windows Platform (UWP). Он работает с использованием Microsoft.Xaml.Interactivity
и Microsoft.Xaml.Interactions.Core
.
Здесь Вид:
<ListView x:Name="ItemsList">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="SelectionChanged">
<Core:InvokeCommandAction Command="{x:Bind ViewModel.SelectedItemsChanged}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<!-- content etc. -->
</ListView>
Здесь ViewModel (RelayCommand
является классом из MVVM Light):
private List<YourType> _selectedItems = new List<YourType>();
private RelayCommand<SelectionChangedEventArgs> _selectedItemsChanged;
public RelayCommand<SelectionChangedEventArgs> SelectedItemsChanged
{
get
{
if (_selectedItemsChanged == null)
_selectedItemsChanged = new RelayCommand<SelectionChangedEventArgs>((selectionChangedArgs) =>
{
// add a guard here to immediatelly return if you are modifying the original collection from code
foreach (var item in selectionChangedArgs.AddedItems)
_selectedItems.Add((YourType)item);
foreach (var item in selectionChangedArgs.RemovedItems)
_selectedItems.Remove((YourType)item);
});
return _selectedItemsChanged;
}
}
Помните, что если вы собираетесь удалить элементы из исходной коллекции после завершения выбора (пользователь нажимает кнопку и т.д.), он также удалит элементы из вашего списка _selectedItems
! Если вы сделаете это в цикле foreach, вы получите InvalidOperationException
. Чтобы этого избежать, просто добавьте охрану в отмеченное место, например:
if (_deletingItems)
return;
а затем в методе, где вы, например, удаляете элементы, выполните следующее:
_deletingItems = true;
foreach (var item in _selectedItems)
YourOriginalCollection.Remove(item);
_deletingItems = false;