2012-06-09 8 views
10

Es gibt einige Beiträge, in denen das Hinzufügen von Datenbindungsmöglichkeiten für ListView.SelectedItems mit nicht-trivialer Code-Menge besprochen wird. In meinem Szenario muss ich es nicht von der ViewModel setzen, nur ausgewählte Elemente, um eine Aktion an ihnen durchzuführen und es wird durch einen Befehl ausgelöst, so dass Push-Update auch nicht notwendig ist.Abrufen von WPF ListView.SelectedItems in ViewModel

Gibt es eine einfache Lösung (in Bezug auf Zeilen des Codes), vielleicht in Code-Behind? Mir geht es gut mit code-behind solange View und ViewModel müssen nicht aufeinander verweisen. Ich denke, das ist eine allgemeinere Frage: "Best Practice für VM, um Daten aus der On-Demand-Ansicht zu erhalten", aber ich kann nichts finden ...

Antwort

23

Um die SelectedItems nur zu erhalten, wenn ein Befehl ausgeführt wird, verwenden Sie CommandParameter und übergeben Sie die ListView.SelectedItems.

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

'SelectedItems' (Plural) unterstützt keine Datenbindung. Siehe [diesen Link] (http://stackoverflow.com/questions/803216/managing-multiple-selections-with-mvvm) und [diesen Link] (http://social.msdn.microsoft.com/forums/en- US/wpf/thread/edd335ea-e5e1-48e1-91a2-793d613f5cc3 /). Es funktioniert auch nicht als 'CommandParameter', ich bekomme immer' null', während 'SelectedItem' (singular) in Ordnung ist. –

+0

@ user986080 Ich habe nicht erkannt, dass 'SelectedItems' die Bindung nicht unterstützt. Ich habe das aus der Antwort entfernt. Wie auch immer, der 'CommandParameter' funktioniert, ich habe ihn getestet und konnte die Liste der ausgewählten Elemente anzeigen. – evanb

+0

Mein XAML-Beispiel zeigt eine 'ListBox', aber ich habe auch eine' ListView' getestet und konnte die ausgewählten Elemente vom Befehlsparameter abrufen. – evanb

2

Ich glaube nicht, dass es korrekt ist "View and ViewModel müssen sich nicht kennen"; In MVVM sehen Sie immer über ViewModel.

ich auch in einer solchen Situation kommen, wo ich Ansichtsmodell in Code Sicht hinter zugreifen musste und dann einige Daten füllen (wie ausgewählte Elemente), dies wird notwendig, bei der Verwendung von 3'rd Party-Steuerelemente wie Listview, DataGrid- etc .

Wenn eine direkte Bindung der VM-Eigenschaft nicht möglich ist, dann würde ich das ListViw.SelectionChanged-Ereignis abhören und dann meine ViewModels SelectedItems-Eigenschaft in diesem Ereignis aktualisieren.

Update:

Damit VM Pull Daten aus Sicht, Sie können eine Schnittstelle auf die Ansicht verfügbar machen, die Griffe View-spezifische Funktionalität und Ansichtsmodell wird anhand Ihrer Ansicht durch diese Schnittstelle haben; Die Verwendung einer Schnittstelle hält das View- und ViewModel weiterhin weitgehend entkoppelt, aber ich bevorzuge das nicht.

MVVM, providing the Association of View to ViewModel

ich noch die approch der Umgang mit der Veranstaltung in Aussicht bevorzugen würde und halten die VM aktualisiert (mit den ausgewählten Elementen), auf diese Weise VM brauchen sich nicht um Ziehen der Daten zu sorgen, bevor eine Operation auszuführen Es müssen nur die verfügbaren Daten verwendet werden (da diese immer aktualisiert werden).

+0

Sorry, ich war nicht klar. Indem ich mich nicht kenne, meine ich, dass man sich nicht auf den anderen bezieht. Auch die Registrierung für das Ereignis 'SelectionChanged' ist nicht unbedingt erforderlich, da das ViewModel nur ausgewählte Elemente abrufen muss, wenn ein Befehl ausgeführt wird. Es ist eher wie "Wie kann VM Daten von der View on demand ziehen". –

8

Diese Interaktion mit erreicht werden kann, löst wie unten

  1. Sie müssen Bezug auf

    Microsoft.Expression.Interactions System.Windows hinzuzufügen.Inter

hinzufügen unter Xmlns zu XAML

xmlns:i="http://schemas.microsoft.com/expression//2010/interactivity" 
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 

Fügen Sie den Code unten nur in Ihrem Gridview-Tag

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

Code in Ansichtsmodell erklären Eigentum unter

public DelegateCommand<object> SelectionChangedCommand {get;set;} 

innerhalb bauen oder von Viewmodel initialisieren Befehl wie unten

SelectionChangedCommand = new DelegateCommand<object> (items => { 
    var itemList = (items as ObservableCollection<object>).Cast<YourDto>().ToList(); 
} 
+0

Ich hatte das gleiche Problemumgehung, – Mihai

2

Ich kann Ihnen versichern: SelectedItems ist in der Tat bindable als XAML Command

Nach viel zu graben und googeln, Ich habe endlich eine einfache Lösung zu diesem häufigen Problem.

  1. Nach Ed Ball's suggestion‘, auf Sie XAML Befehl Databinding, definieren Command Eigenschaft vor Befehl Eigenschaft:

    Um es müssen Siewerden alle folgenden Regeln arbeiten. Dies ist ein sehr zeitaufwendiger Fehler.

  2. Stellen Sie sicher, Ihre ICommand ‚s CanExecute und Execute-Methoden einen Parameter Objekt Typ haben. Auf diese Weise können Sie verhindern, dass stummgeschaltete Ausnahmebedingungen auftreten, die immer dann auftreten, wenn die Datenbindung CommandParameter nicht mit dem Parametertyp der Befehlsmethode übereinstimmt.

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

Zum Beispiel können Sie entweder ein Listview/Listbox der SelectedItems Eigenschaft senden Sie ICommand Methoden oder die Listview/Listbox selbst ein. Großartig, nicht wahr?

Hoffe, dass es verhindert, dass jemand die riesige Menge an Zeit verbringen ich herauszufinden, tat, wie SelectedItems als CanExecute Parameter zu empfangen.

0

Da keiner der anderen Antworten mir geholfen (mit SelectedItems als CommandParameter immer null war), hier ist eine Lösung für Universal Windows-Plattform (UWP) Anwendungen. Es funktioniert mit Microsoft.Xaml.Interactivity und Microsoft.Xaml.Interactions.Core.

Hier ist die Aussicht:

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

Hier ist das Viewmodel (RelayCommand ist eine Klasse von 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; 
    } 
} 

Passen Sie auf, wenn Sie Elemente aus der ursprünglichen Sammlung nach dem entfernen gehen Die Auswahl ist abgeschlossen (der Benutzer drückt einen Knopf usw.), es werden auch die Einträge aus Ihrer _selectedItems Liste entfernt! Wenn Sie dies in einer foreach-Schleife tun, erhalten Sie eine InvalidOperationException. Um dies zu vermeiden, fügen Sie einfach eine Wache in der markierten Stelle wie:

if (_deletingItems) 
    return; 

und dann in dem Verfahren, in dem Sie zum Beispiel der Elemente entfernen, dies zu tun:

_deletingItems = true; 
foreach (var item in _selectedItems) 
    YourOriginalCollection.Remove(item); 
_deletingItems = false; 
Verwandte Themen