Sélectionner plusieurs éléments d'une DataGrid dans un projet MVVM WPF

Comment puis-je sélectionner plusieurs éléments d'un DataGrid dans un projet du FPF MVVM?

53
demandé sur DavidRR 2014-04-04 20:42:45

8 réponses

, Vous pouvez simplement ajouter un personnalisé propriété de dépendance pour ce faire:

public class CustomDataGrid : DataGrid
{
    public CustomDataGrid ()
    {
        this.SelectionChanged += CustomDataGrid_SelectionChanged;
    }

    void CustomDataGrid_SelectionChanged (object sender, SelectionChangedEventArgs e)
    {
        this.SelectedItemsList = this.SelectedItems;
    }
    #region SelectedItemsList

    public IList SelectedItemsList
    {
        get { return (IList)GetValue (SelectedItemsListProperty); }
        set { SetValue (SelectedItemsListProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsListProperty =
            DependencyProperty.Register ("SelectedItemsList", typeof (IList), typeof (CustomDataGrid), new PropertyMetadata (null));

    #endregion
}

Maintenant vous pouvez utiliser ce dataGrid dans le XAML:

<Window x:Class="DataGridTesting.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:local="clr-namespace:DataGridTesting.CustomDatagrid"
    Title="MainWindow"
    Height="350"
    Width="525">
  <DockPanel>
    <local:CustomDataGrid ItemsSource="{Binding Model}"
        SelectionMode="Extended"
        AlternatingRowBackground="Aquamarine"
        SelectionUnit="FullRow"
        IsReadOnly="True"
        SnapsToDevicePixels="True"
        SelectedItemsList="{Binding TestSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
  </DockPanel>
</Window>

Mon ViewModel :

public class MyViewModel : INotifyPropertyChanged
{
    private static object _lock = new object ();
    private List<MyModel> _myModel;

    public IEnumerable<MyModel> Model { get { return _myModel; } }

    private IList _selectedModels = new ArrayList ();

    public IList TestSelected
    {
        get { return _selectedModels; }
        set
        {
            _selectedModels = value;
            RaisePropertyChanged ("TestSelected");
        }
    }

    public MyViewModel ()
    {
        _myModel = new List<MyModel> ();
        BindingOperations.EnableCollectionSynchronization (_myModel, _lock);

        for (int i = 0; i < 10; i++)
        {
            _myModel.Add (new MyModel
            {
                Name = "Test " + i,
                Age = i * 22
            });
        }
        RaisePropertyChanged ("Model");
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged (string propertyName)
    {
        var pc = PropertyChanged;
        if (pc != null)
            pc (this, new PropertyChangedEventArgs (propertyName));
    }
}

mon modèle:

public class MyModel
{
    public string Name { get; set; }
    public int Age { get; set; }
}

et enfin, voici le code derrière MainWindow :

public partial class MainWindow : Window
{
    public MainWindow ()
    {
        InitializeComponent ();
        this.DataContext = new MyViewModel ();
    }
}

j'espère que cette conception propre MVVM aide.

96
répondu Sandesh 2017-01-13 13:49:22

ce que je ferais c'est créer Behaviors en utilisant System.Windows.Interactivity . Vous devez le référencer manuellement dans votre projet.

avec une commande qui n'expose pas SelectedItems p.ex., (ListBox, DataGrid)

vous pouvez créer une classe de comportement quelque chose comme ça

public class ListBoxSelectedItemsBehavior : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        AssociatedObject.SelectionChanged += AssociatedObjectSelectionChanged;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.SelectionChanged -= AssociatedObjectSelectionChanged;
    }

    void AssociatedObjectSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var array = new object[AssociatedObject.SelectedItems.Count];
        AssociatedObject.SelectedItems.CopyTo(array, 0);
        SelectedItems = array;
    }

    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.Register("SelectedItems", typeof(IEnumerable), typeof(ListBoxSelectedItemsBehavior), 
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public IEnumerable SelectedItems
    {
        get { return (IEnumerable)GetValue(SelectedItemsProperty); }
        set { SetValue(SelectedItemsProperty, value); }
    }
}

et sur votre XAML je ferais le Binding comme ceci où i est xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" et behaviors est espace de noms de votre Behavior classe

<ListBox>
 <i:Interaction.Behaviors>
    <behaviors:ListBoxSelectedItemsBehavior SelectedItems="{Binding SelectedItems, Mode=OneWayToSource}" />
 </i:Interaction.Behaviors>

en supposant que votre DataContext pour le ListBox a la propriété SelectedItems dans le ViewModel alors il mettra automatiquement à jour le SelectedItems . Vous avez encapsulé le event souscrivant du View i.e.,

<ListBox SelectionChanged="ListBox_SelectionChanged"/>

vous pouvez changer la classe Behavior pour être de type DataGrid si vous voulez.

26
répondu 123 456 789 0 2014-04-09 20:56:47

j'utilise cette solution dans mon application:

XAML:

<i:Interaction.Triggers>
     <i:EventTrigger EventName="SelectionChanged">
         <i:InvokeCommandAction Command="{Binding SelectItemsCommand}" CommandParameter="{Binding Path=SelectedItems,ElementName=TestListView}"/>
     </i:EventTrigger>
</i:Interaction.Triggers>

au haut de votre fichier xaml, Ajoutez cette ligne de code:

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

SelectedItemsCommand est le type ICommand qui est écrit dans votre modèle de vue.

DLL utilisée:

Système

.Windows.Interactivité.dll

15
répondu Allen4Tech 2014-04-08 03:05:08

avec la valeur par défaut DataGrid de WPF il n'est pas possible d'utiliser une reliure, car il est possible avec la propriété SelectedItem -, car la propriété SelectedItems - N'est pas une propriété de dépendance.

une façon d'accéder à ce que vous voulez est d'enregistrer le SelectionChanged - Événement du DataGrid pour mettre à jour la propriété de votre ViewModel, qui stocke les éléments sélectionnés.

La propriété SelectedItems de la grille de données est de type IList donc vous devez mouler les éléments dans la liste à votre type spécifique.

C#

public MyViewModel {
  get{
    return this.DataContext as MyViewModel;
  }
}

private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) {
  // ... Get SelectedItems from DataGrid.
  var grid = sender as DataGrid;
  var selected = grid.SelectedItems;

  List<MyObject> selectedObjects = selected.OfType<MyObject>().ToList();

  MyViewModel.SelectedMyObjects = selectedObjects;
}

XAML

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
    <DataGrid
        SelectionChanged="DataGrid_SelectionChanged"
        />
    </Grid>
</Window>
9
répondu Jehof 2014-04-07 06:13:09

vous pouvez ajouter la propriété" IsSelected " dans le Modèle et ajouter une case à cocher dans la rangée.

2
répondu Angular_Learn 2014-04-09 18:32:03

vous pouvez faire un réutilisable classe de base générique . De cette façon, vous pouvez sélectionner les lignes à la fois du code et de L'UI .

C'est ma classe d'exemple je veux être sélectionnable

public class MyClass
{
    public string MyString {get; set;}   
}

faire classe de base générique pour les classes sélectionnables. INotifyPropertyChanged fait la mise à jour de L'interface utilisateur lorsque vous définissez IsSelected.

public class SelectableItem<T> : System.ComponentModel.INotifyPropertyChanged
{
    public SelectableItem(T item)
    {
        Item = item;
    }

    public T Item { get; set; }

    bool _isSelected;

    public bool IsSelected {
        get {
            return _isSelected;
        }
        set {
            if (value == _isSelected)
            {
                return;
            }

            _isSelected = value;

            if (PropertyChanged != null)
            { 
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("IsSelected"));
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}

créer classe sélectionnable

public class MySelectableItem: SelectableItem<MyClass>
{
    public MySelectableItem(MyClass item)
       :base(item)
    {
    }
}

Créer de la propriété de se lier à

ObservableCollection<MySelectableItem> MyObservableCollection ...

set propety

MyObservableCollection = myItems.Select(x => new MySelectableItem(x));

lier à datagrid et Ajouter un style sur le DataGridRow qui se lie à la propriété Issélectionnée sur le MySelectedItem

<DataGrid  
    ItemsSource="{Binding MyObservableCollection}"
    SelectionMode="Extended">
    <DataGrid.Resources>
        <Style TargetType="DataGridRow">
            <Setter Property="IsSelected" Value="{Binding IsSelected}" />
        </Style>
    </DataGrid.Resources>
</DataGrid>

Pour obtenir des lignes sélectionnées/articles

var selectedItems = MyObservableCollection.Where(x=>x.IsSelected).Select(y=>y.Item);

pour sélectionner les lignes /éléments

MyObservableCollection[0].IsSelected = true;

Edit - - -> Il semble que cela ne fonctionne pas lorsque la virtualisation Enablerow est vrai

2
répondu AxdorphCoder 2018-01-03 16:09:23

WPF DataGrid permet cela. Il suffit de définir le DataGrid.Rangée.SelectionMode et DataGrid.Rangée.SelectionUnit à "Extended" et" CellOrRowHeader " respectivement. Cela peut être fait en mélange, comme montré dans l'image que j'ai inclus. Cela permettra à l'utilisateur de sélectionner chaque cellule, des rangées entières etc. autant qu'ils le souhaitent, en utilisant la touche shift ou ctrl pour continuer à sélectionner. enter image description here

0
répondu dylansweb 2014-04-08 02:48:03

le projet sur lequel je travaille utilise MVVM Light et j'ai trouvé ce blog post pour être la solution la plus simple. Je répète la solution ici:

Modèle De Vue:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
...

public class SomeVm : ViewModelBase {

    public SomeVm() {
        SelectItemsCommand = new RelayCommand<IList>((items) => {
            Selected.Clear();
            foreach (var item in items) Selected.Add((SomeClass)item);
        });

        ViewCommand = new RelayCommand(() => {
            foreach (var selected in Selected) {
                // todo do something with selected items
            }
        });
    }

    public List<SomeClass> Selected { get; set; }
    public RelayCommand<IList> SelectionChangedCommand { get; set; }
    public RelayCommand ViewCommand { get; set; }
}

XAML:

<Window
    ...
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:command="http://www.galasoft.ch/mvvmlight"
    ...
    <DataGrid
        Name="SomeGrid"
        ...
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <command:EventToCommand
                    Command="{Binding SelectionChangedCommand}"
                    CommandParameter="{Binding SelectedItems, ElementName=SomeGrid}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
        ...
        <DataGrid.ContextMenu>
            <ContextMenu>
                <MenuItem Header="View" Command="{Binding ViewCommand}" />
            </ContextMenu>
        </DataGrid.ContextMenu>
        ...
0
répondu Seth Reno 2017-12-19 14:51:55