Удалить SelectedItems из ListBox через MVVM RelayCommand - PullRequest
1 голос
/ 10 мая 2010

У меня есть список элементов в WPF ListBox. Я хочу позволить пользователю выбрать несколько из этих элементов и нажать кнопку «Удалить», чтобы удалить эти элементы из списка.

Используя шаблон MVVM RelayCommand, я создал команду со следующей подписью:

public RelayCommand<IList> RemoveTagsCommand { get; private set; }

В моем представлении я подключаю команду RemoveTagsCommand следующим образом:

<DockPanel>
<Button DockPanel.Dock="Right" Command="{Binding RemoveTagsCommand}" CommandParameter="{Binding ElementName=TagList, Path=SelectedItems}">Remove tags</Button>
<ListBox x:Name="TagList" ItemsSource="{Binding Tags}" SelectionMode="Extended">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.Resources>
        <DataTemplate DataType="{x:Type Model:Tag}">
            ...
        </DataTemplate>
    </ListBox.Resources>
</ListBox>
</DockPanel>

Мой конструктор ViewModel устанавливает экземпляр команды:

RemoveTagsCommand = new RelayCommand<IList>(RemoveTags, CanRemoveTags);

Моя текущая реализация RemoveTags кажется неуклюжей, с приведениями и копированием. Есть ли лучший способ реализовать это?

    public void RemoveTags(IList toRemove)
    {
        var collection = toRemove.Cast<Tag>();
        List<Tag> copy = new List<Tag>(collection);

        foreach (Tag tag in copy)
        {
            Tags.Remove(tag);
        }
    }

Ответы [ 4 ]

4 голосов
/ 10 мая 2010

Я бы использовал ItemContainerStyle в ListBox, чтобы связать свойство IsSelected элементов с флагом в модели (не в модели просмотра), например ::10000 *

 <ListBox.ItemContainerStyle> 
    <Style TargetType="{x:Type ListBoxItem}">  
      <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>  
    </Style> 
 </ListBox.ItemContainerStyle>

Тогда вам не нужно беспокоиться о том, какой аргумент вы передаете своей команде. Кроме того, по моему опыту, когда объекту в модели представления просто узнать, что его выбрал пользователь, вы найдете другие варианты использования этой информации.

Код в команде будет выглядеть примерно так:

foreach (Tag t in Tags.Where(x => x.IsSelected).ToList())
{
   Tags.Remove(t);
}
2 голосов
/ 10 мая 2010

Это выглядит довольно чисто для меня, хотя вы можете связать SelectedItems со свойством вашей виртуальной машины, используя Mode=OneWayToSource, а затем использовать свойство связанной коллекции из RemoveTags.
полностью уверен, но в этом случае вы можете использовать строго типизированную коллекцию IList.

0 голосов
/ 10 мая 2010

1.) Свяжите кнопку «Удалить» с командой в вашей ViewModel.

2.) Когда вы устанавливаете привязку, используйте CommandParameter, чтобы взять Selecteditems из вашего ListBox, дав вашему ListBox имя и используя ElementName = NameOfListBox, Path = SelectedItems

3.) Убедитесь, что ваша команда в вашей ViewModel передает аргументы. Вы получите объект, который можете разыграть как IList.

Ниже приведен простой пример, который должен помочь вам настроить вашу структуру.

В представлении:

<Button Command="{Binding CommandInViewModelForRemove}"
        CommandParameter="{Binding ElementName=blah,Path=SelectedItems}"

<ListBox x:Name="blah" .... />

В ViewModel:

public ViewModel(){
    RemoveCommand = new RelayCommand<object>(Remove, CanRemove);
}

private void Remove(object selectedItems){
   var list = (IList)selectedItems;
   //do some work, cast to view models that represent list items, etc
}

Надеюсь, это поможет!

0 голосов
/ 10 мая 2010

Почему бы вам не указать аргумент типа RelayCommand как List<Tag>, поскольку это все равно вы получите? Нет смысла указывать более общий тип, чем тот, потому что исполняемый обработчик жестко запрограммирован для работы со списком Tag объектов. Поскольку вы уже сделали зависимость там, вы можете также сделать это с аргументом типа. Тогда исполняемый обработчик не будет нуждаться ни в приведении, ни в копировании.

...