2009-04-29 13 views
55

Podczas mojej podróży do nauki MVVM poznałem podstawową wiedzę na temat WPF i wzorca ViewModel. Używam poniższej abstrakcji, gdy dostarczam listę i interesuje mnie jeden wybrany przedmiot.Zarządzanie wieloma wyborami za pomocą MVVM

public ObservableCollection<OrderViewModel> Orders { get; private set; } 
public ICollectionView OrdersView 
{ 
    get 
    { 
     if(_ordersView == null) 
      _ordersView = CollectionViewSource.GetDefaultView(Orders); 
     return _ordersView; 
    } 
} 
private ICollectionView _ordersView; 

public OrderViewModel CurrentOrder 
{ 
    get { return OrdersView.CurrentItem as OrderViewModel; } 
    set { OrdersView.MoveCurrentTo(value); } 
} 

mogę następnie powiązać OrdersView wraz ze wspierającą sortowania i filtrowania do listy w WPF:

<ListView ItemsSource="{Binding Path=OrdersView}" 
      IsSynchronizedWithCurrentItem="True"> 

To działa bardzo dobrze dla pojedynczych widoków selekcji. Ale chciałbym również obsługiwać wiele wyborów w widoku i powiązać model z listą wybranych elementów.

W jaki sposób powiązać ListView.SelectedItems z właściwością backer na ViewModel?

Odpowiedz

92

Dodaj właściwość IsSelected dziecku ViewModel (OrderViewModel w danym przypadku):

public bool IsSelected { get; set; } 

Bind wybrany obiekt w pojemniku do tego (dla pola listy w tym przypadku):

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

IsSelected jest aktualizowany, aby pasował do odpowiedniego pola w kontenerze.

można uzyskać wybrane dzieci w modelu widoku w następujący sposób:

public IEnumerable<OrderViewModel> SelectedOrders 
{ 
    get { return Orders.Where(o => o.IsSelected); } 
} 
+29

Należy zauważyć, że to rozwiązanie nie działa, gdy używany jest VirtualizingStackPanel w liście listBox (która jest domyślna). Więcej informacji pod tym postem: http://stackoverflow.com/questions/1273659/virtualizingstackpanel-mvvm-multiple-selection – decasteljau

+3

Dobry połów. Dziękuję za aktualizację. W przypadku pojedynczego wyboru najlepszym rozwiązaniem jest ICollectionView. Microsoft musi stworzyć interfejs ICollectionView, który obsługuje selekcje wielokrotne. –

+0

Właśnie uratowałeś mi życie! – jpsstavares

1

Jeśli używasz MVVM-Light można użyć tego wzoru:

http://blog.galasoft.ch/archive/2010/05/19/handling-datagrid.selecteditems-in-an-mvvm-friendly-manner.aspx

Nie szczególnie elegancki, ale wygląda na to, że powinien być niezawodny co najmniej:

+0

hmm faktycznie działa tylko w jedną stronę dzięki tej metodzie (widok> widok modelu). niezdolny do widoku modelu> widok opisany tutaj –

+2

Chociaż może to teoretycznie odpowiedzieć na pytanie, [byłoby wskazane] (http://meta.stackexchange.com/q/8259) do włączenia istotnych części odpowiedzi tutaj, i podaj link dla odniesienia. – Kev

4

Można spróbować utworzyć dołączoną właściwość.

Wykonanie tej czynności spowoduje zapisanie jednego z dodania właściwości IsSelected dla każdej powiązanej listy. Zrobiłem to dla ListBox, ale można go zmodyfikować do użycia w widoku listy.

<ListBox SelectionMode="Multiple" 
     local:ListBoxMultipleSelection.SelectedItems="{Binding SelectedItems}" > 

Więcej informacji: WPF – Binding ListBox SelectedItems – Attached Property VS Style.

+1

Bardzo lubię twoje rozwiązanie, jednak musiałem zmodyfikować twój kod tak, aby nie buforował listy w załączonej nieruchomości. Buforowanie pola listy uniemożliwia korzystanie z dołączonej właściwości w innych polach listy. – Darkhydro

11

mogę was zapewnić: SelectedItems jest rzeczywiście Bindable jako XAML CommandParameter

Istnieje proste rozwiązanie tego wspólnego problemu; aby to działało trzeba przestrzegać ALL następujące zasady:

  1. Po Ed Ball's suggestion, na XAML polecenia wiązania z danymi, należy zdefiniować atrybut CommandParameter PRZED atrybutu Command. To bardzo czasochłonny błąd.

    enter image description here

  2. Upewnij się mieć swoje ICommand „s CanExecute i Execute metody parametr typu object. W ten sposób można zapobiec uciszeni lane wyjątków, które występują zawsze, gdy wiązania z danymi za CommandParameter typ nie pasuje do Twojego typu parametru Command metoda to:

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

Na przykład, można też wysłać ListView/ListBox „s SelectedItems własność do twoich metod ICommand lub samej. Świetnie, prawda?

Mam nadzieję, że uniemożliwi to komuś spędzanie ogromnej ilości czasu, aby dowiedzieć się, jak otrzymać SelectedItems jako parametr CanExecute.

+2

Po prostu próbowałem to z DataGrid, używając właściwości SelectedItems w ten sposób Stwierdziłem, że CanExecute został wywołany z wartością _previous_, jeśli SelectedItems (tj. Nie odzwierciedla bieżącego stanu wyboru). Może być inny dla innych kontroli, ale okazał się bezużyteczny w tym przypadku. –

+1

Dobrze mi działało (zarówno dla ListBox, jak i DataGrid, przy użyciu .Net 4.0). Miałem Command i CommandParameter w odwrotnej kolejności do tego, co zostało określone i nadal działało. – BCA

+2

ładne, idealne do wyświetlenia listy w 4.5.2. Należy dodać, że użyłem również DelegateCommand od Prism for the Execute i CanExecute.so: 'ICommand btnCommand = new DelegateCommand (Execute, CanExecute);' – CularBytes

Powiązane problemy