Получение WPF ListView.SelectedItems в ViewModel

есть несколько сообщений, обсуждающих добавление способности привязки данных для ListView.SelectedItems с нетривиальным количеством кода. В моем сценарии мне не нужно устанавливать его из ViewModel, просто получение выбранных элементов для выполнения действий над ними, и это запускается командой, поэтому push update также не требуется.

есть ли простое решение (с точки зрения строк кода), возможно, в коде? Я в порядке с кодом, пока View и ViewModel Не надо ссылаться друг на друга. Я думаю, что это более общий вопрос: "лучшая практика для VM для получения данных из представления по требованию

5 ответов


для получения SelectedItems только при выполнении команды используйте CommandParameter и передать ListView.SelectedItems.

<ListBox x:Name="listbox" ItemsSource="{Binding StringList}" SelectionMode="Multiple"/>
<Button Command="{Binding GetListItemsCommand}" CommandParameter="{Binding SelectedItems, ElementName=listbox}" Content="GetSelectedListBoxItems"/>

это может быть достигнуто с помощью триггеров взаимодействия, как показано ниже

  1. вам нужно будет добавить ссылку на

    Microsoft.Выражение.Взаимодействия Система.Окна.Интерактивность

добавить ниже 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>

код внутри ViewModel объявить свойство ниже

public DelegateCommand<object> SelectionChangedCommand {get;set;}

в конструкторе Viewmodel инициализировать Команда, как показано ниже

SelectionChangedCommand = new DelegateCommand<object> (items => {
   var itemList = (items as ObservableCollection<object>).Cast<YourDto>().ToList();
}

могу вас заверить:SelectedItems действительно связывается как XAML CommandParameter

после долгих поисков и поиска в гугле я наконец нашел простое решение этой общей проблемы.

чтобы заставить его работать, вы должны следуйте всем следующим правилам:

  1. после предложение Эда Болла', на вас XAML команда databinding, определить CommandParameter собственность До команда собственность. Это очень трудоемкая ошибка.

  2. убедитесь, что ваш ICommand ' s CanExecute, чтобы и выполнить методы имеют параметр объект тип. Таким образом, вы можете предотвратить притихли исключения cast, которые возникают при привязке данных CommandParameter type не соответствует типу параметра вашего метода команды.

    private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)  
    {
        // Your goes heres
    }
    
    private bool OnDeleteSelectedItemsExecute(object SelectedItems)  
    {
        // Your goes heres
    }
    

например, вы можете отправить ListView/listbox в SelectedItems собственность ICommand методы или listview / listbox он сам. Здорово, правда?

надеюсь, что это мешает кому-то тратить огромное количество времени, которое я сделал, чтобы выяснить, как получить SelectedItems as CanExecute, чтобы


Я не думаю, что это правильное условие, чтобы считать, что 'View и ViewModel не должны знать друг друга'; в представлении MVVM всегда знайте о ViewModel.

Я также столкнулся с такой ситуацией, когда мне пришлось получить доступ к ViewModel в коде view, а затем заполнить некоторые данные(например, выбранные элементы), это становится необходимым при использовании 3-х партийных элементов управления, таких как ListView, DataGrid и т. д.

Если сразу связывать свойство VM невозможно, тогда я бы слушал ListViw.SelectionChanged событие, а затем обновить мои ViewModels SelectedItems свойство в этом событии.

обновление:

чтобы включить VM pull data from view, вы можете предоставить интерфейс на представлении, который обрабатывает функциональность представления, и ViewModel будет иметь ссылку на ваш вид через этот интерфейс; использование интерфейса по-прежнему сохраняет представление и ViewModel в значительной степени развязаны, но я в целом не предпочитаю этот.

MVVM, предоставляя Ассоциацию View для ViewModel

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


так как ни один из других ответов не помог мне (используя SelectedItems as CommandParameter всегда null), вот решение для приложений универсальной платформы Windows (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;