2012-06-09 4 views
10

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

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

ответ

23

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

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

'SelectedItems' (множественное число) не поддерживает привязку данных. См. [Эта ссылка] (http://stackoverflow.com/questions/803216/managing-multiple-selections-with-mvvm) и [эта ссылка] (http://social.msdn.microsoft.com/forums/en- США/МОФ/резьба/edd335ea-e5e1-48e1-91a2-793d613f5cc3 /). Он также не работает как «CommandParameter», я всегда получаю «null», в то время как использование «SelectedItem» (единственного числа) отлично. –

+0

@ user986080 Я не понимал, что 'SelectedItems' не поддерживает привязку. Я удалил это из ответа. Однако 'CommandParameter' работает, я его протестировал и смог выбрать список выбранных элементов. – evanb

+0

В моем примере XAML отображается «ListBox», но я также проверил «ListView» и смог получить выбранные элементы из параметра команды. – evanb

2

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

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

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

Update:

Чтобы включить VM тянуть данные из поля зрения, Вы можете выставить интерфейс на View, который обрабатывает специфичную функциональность и ViewModel будет иметь ссылку на Ваш взгляд через этот интерфейс; Использование интерфейса по-прежнему удерживает View и ViewModel в значительной степени развязанным, но я в общем не предпочитаю этого.

MVVM, providing the Association of View to ViewModel

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

+0

К сожалению, я не был ясен. Не зная друг друга, я имею в виду, что никто не ссылается на другого. Кроме того, регистрация в событии «SelectionChanged» не является обязательной, так как ViewModel должен получать только выбранные элементы при выполнении команды. Это больше похоже на «Как заставить VM отображать данные из представления по требованию». –

8

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

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

    Microsoft.Expression.Interactions System.Windows.Интерактивность

Добавить ниже 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(); 
} 
+0

У меня был тот же обходной путь, – Mihai

2

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

После много рытья и прибегая к помощи, я наконец-то нашел простое решение к этой общей проблеме.

Чтобы сделать его работу вы должны следовать ВСЕМ следующим правилам:

  1. После Ed Ball's suggestion», на вас XAML командах привязок данных, определить CommandParameter свойства ДО Командования недвижимости. Это очень трудоемкая ошибка.

  2. Убедитесь, что ICommand «s CanExecute и Execute методы имеют параметр объекта типа. Таким образом вы можете предотвратить отключен исключений исключения, возникающих при привязке данных Тип 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 параметра.

0

Поскольку ни один из ответов не помог мне (используя SelectedItems, так как 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; 
Смежные вопросы