2010-05-10 24 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