How can I select multiple items from a DataGrid
in an MVVM WPF project?
A Grid is a control for laying out other controls on the form (or page). A DataGrid is a control for displaying tabular data as read from a database for example.
WPF DataGrid (SfDataGrid) provides built-in row called AddNewRow. It allows user to add a new row to underlying collection. You can enable or disable by setting SfDataGrid.
A DataGrid is a control that displays data in a customizable grid. It provides a flexible way to display a collection of data in rows and columns. The hierarchical inheritance of DataGrid class is as follows −
You can simply add a custom dependency property to do this:
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 }
Now you can use this dataGrid
in the 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>
My 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)); } }
My model:
public class MyModel { public string Name { get; set; } public int Age { get; set; } }
And finally, here is the code behind of MainWindow
:
public partial class MainWindow : Window { public MainWindow () { InitializeComponent (); this.DataContext = new MyViewModel (); } }
I hope this clean MVVM design helps.
What I would do is create Behaviors
using System.Windows.Interactivity
. You would have to reference it manually in your project.
Given a control which doesn't expose SelectedItems
e.g., (ListBox, DataGrid)
You can create a behavior class something like this
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); } } }
And on your XAML
I would do the Binding
like this where i
is xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
and behaviors
is the namespace of your Behavior
class
<ListBox> <i:Interaction.Behaviors> <behaviors:ListBoxSelectedItemsBehavior SelectedItems="{Binding SelectedItems, Mode=OneWayToSource}" /> </i:Interaction.Behaviors>
Assuming that your DataContext
for the ListBox
has the SelectedItems
property in the ViewModel
then it will automatically update the SelectedItems
. You have encapsulated the event
subscribing from the View
i.e.,
<ListBox SelectionChanged="ListBox_SelectionChanged"/>
You can change the Behavior
class to be of type DataGrid
if you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With