2010-05-10 43 views
0

我有一个WPF列表框中的项目列表。我想让用户选择其中的几个项目,然后点击删除按钮从列表中删除这些项目。通过MVVM RelayCommand从列表框中删除SelectedItems

使用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> 

我的视图模型构造设置该命令的一个实例:

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

我目前的工具a删除标签的感觉很笨拙,并进行了强制转换和复制。有没有更好的方法来实现这一点?

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); 
     } 
    } 

回答

2

这对我来说看起来相当干净,虽然您可能能够使用Mode = OneWayToSource将SelectedItems绑定到VM上的属性,然后使用RemoveTags中的绑定集合属性。我不完全确定,但在这种情况下,您可能会使用强类型的IList集合。

+0

不幸的是,我不能使用强类型的IList。我认为这是WPF返回的SelectedItemCollection的限制,但也可能是由于我使用的RelayCommand的特殊风格。 – dthrasher 2010-05-10 20:24:28

+0

再看一遍,我发现真正的担忧是SelectedItems列表可能会因删除而改变,这就是为什么您要创建副本。如果是这样的话,我认为这个代码就像你会得到的一样干净。除非你想让你的RelayCommand在内部支持演员(类似于'RelayListCommand '和一个隐含的'Cast ''将IList'修改为'IList ''),否则该演员是不可避免的。 – 2010-05-10 20:36:46

+0

谢谢,丹。你是对的 - 复制和铸造感觉有点笨拙,但它完成了工作。 – dthrasher 2010-06-10 01:26:04

0

你为什么不指定RelayCommand的类型参数是一个List<Tag>,因为这是你会得到什么呢?没有指定更通用的类型,因为执行的处理程序是硬编码的,可与Tag对象列表配合使用。既然你已经在那里创建了依赖关系,你也可以在类型参数上做它。然后你执行的处理程序将不需要任何投射或复制。

+1

我不相信SelectedItems将列表,即使您只添加标记实例。 – 2010-05-10 16:24:06

+0

@丹是对的;我不能在这里使用列表。我认为SelectedItems属性返回一个非泛型的SelectedItemsCollection对象。 – dthrasher 2010-05-10 20:33:53

4

我会用ItemContainerStyleListBox到项目IsSelected属性绑定到一个标志的模型(不视图模型),例如:

<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); 
} 
+0

嗯。这是一个非常有趣的方法。不过,我不确定让UI状态泄漏到我的ViewModel中。 – dthrasher 2010-05-27 15:20:00

+1

这对我来说没有意义。视图模型是视图的模型。它对于了解UI状态似乎完全合适。如果它公开一个命令来删除选定的项目,它肯定应该知道选定的项目是什么。您可能会重新调整视图,以便它使用“ListView”而不是“ListBox”,但不会对其进行重新设置,以免它让用户选择要删除的项目。 – 2010-05-27 16:09:03

+1

我认为这应该被评为正确的答案。 – webe0316 2013-03-07 16:05:29

0

1)绑定您的删除按钮命令在你的视图模型。

2)当您设置绑定,使用CommandParameter通过给你的列表框的名称和使用的ElementName = NameOfListBox,路径= SelectedItems从繁重的ListBox中的Selecteditems

3)确保你的命令在你的ViewModel中传递参数。你会得到一个可以作为IList投射的对象。

下面是一个简单的例子,这应该有助于您设置您的结构。

在查看:

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

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

在视图模型:

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

我实际上在做你已经建议的事情。我的问题是关于您的示例中的“做一些工作”位。有没有一种更清洁,更好的方式来从列表中删除项目,而无需投射每个项目并将selectedItems复制到新列表中? – dthrasher 2010-05-10 20:20:01

+0

哇,我完全错过了 - 对不起!让我回头看看绘图板,看看我想出了什么。干杯! – 2010-05-11 01:52:02