Binding multi selected items in MVVM

7 posts, 1 answers
  1. Maurice
    Maurice avatar
    3 posts
    Member since:
    Apr 2016

    Posted 24 Jan Link to this post

    Hi,

    I want to bind the selecteditems. It works when selectionMode = single. But I want to use Multiple rows with SelectionMode="Multiple"

    I tried to use a ObservableCollection for SelectedItem but that did not work.

    This works with single item.
    <telerikGrid:RadDataGrid  ItemsSource="{Binding GridSource}" SelectedItem="{Binding SelectedItem}">

     

    Any solution for this?

    Thank you!

  2. Lance | Tech Support Engineer, Sr.
    Admin
    Lance | Tech Support Engineer, Sr. avatar
    457 posts

    Posted 24 Jan Link to this post

    Hello Maurice,

    The reason you'renot able to bind to SelectedItems directly is because it is a read-only collection.

    One option you can use to accomplish the same thing is take advantage of UWP's x:Bind to feature of binding to an event handler in the ViewModel.

    <grid:RadDataGrid ItemsSource="{Binding GridItems}"
        SelectionChanged="{x:Bind ViewModel.RadDataGrid_OnSelectionChanged}"
        SelectionMode="Multiple"/>


    This will let you maintain MVVM and still use the DataGrid's SelectionChanged args (e.AddedItems and e.RemovedItems) .

    I've put together a small demo where you can make multiple selection in the DataGrid and you'll see those selections in a RadListView to the right. The ListView is bound to the ViewModel's "SelectedGridItems" property. As you select and deselect items, SelectedGridItems will be updated.

    I've attached the demo and here's a screenshot at runtime:





    Behavior Based Approach

    Another approach you can take is to use XAML Behaviors. This will also sync the SelectedItems between the DataGrid and your ViewModel's SelectedGridItems in an MVVM friendly manner.

    Here's the XAML for the DataGrid using a behavior:

    <grid:RadDataGrid x:Name="DataGrid"
                ItemsSource="{Binding GridItems}"
                SelectionMode="Multiple">
        <interactivity:Interaction.Behaviors>
            <behaviors:MyMultiSelectBehavior SelectedItems="{x:Bind ViewModel.SelelectedGridItems}" />
        </interactivity:Interaction.Behaviors>
    </grid:RadDataGrid>


    And here's the MyMultiSelectBehavior behavior class (Important, you need to add the Microsoft.Xaml.Behaviors.Uwp.Managed Nuget package to the project):

    public class MyMultiSelectBehavior : Behavior<RadDataGrid>
        {
            private RadDataGrid Grid => AssociatedObject as RadDataGrid;
     
            public INotifyCollectionChanged SelectedItems
            {
                get { return (INotifyCollectionChanged)GetValue(SelectedItemsProperty); }
                set { SetValue(SelectedItemsProperty, value); }
            }
     
            public static readonly DependencyProperty SelectedItemsProperty =
                      DependencyProperty.Register("SelectedItems",
                          typeof(INotifyCollectionChanged),
                          typeof(MyMultiSelectBehavior),
                          new PropertyMetadata(default(INotifyCollectionChanged), OnSelectedItemsPropertyChanged));
     
     
            private static void OnSelectedItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var collection = e.NewValue as INotifyCollectionChanged;
                if (collection != null)
                {
                    collection.CollectionChanged += ((MyMultiSelectBehavior)d).Context_CollectionChanged;
                }
            }
     
            protected override void OnAttached()
            {
                base.OnAttached();
     
                Grid.SelectedItems.CollectionChanged += SelectedItems_CollectionChanged;
            }
     
            private void SelectedItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                UnsubscribeFromEvents();
     
                Transfer(Grid.SelectedItems, SelectedItems as IList);
     
                SubscribeToEvents();
            }
     
            private void Context_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                UnsubscribeFromEvents();
     
                Transfer(Grid.SelectedItems, SelectedItems as IList);
     
                SubscribeToEvents();
            }
     
            private void SubscribeToEvents()
            {
                Grid.SelectedItems.CollectionChanged += SelectedItems_CollectionChanged;
     
                if (SelectedItems != null)
                {
                    SelectedItems.CollectionChanged += Context_CollectionChanged;
                }
            }
     
            private void UnsubscribeFromEvents()
            {
                Grid.SelectedItems.CollectionChanged -= SelectedItems_CollectionChanged;
     
                if (SelectedItems != null)
                {
                    SelectedItems.CollectionChanged -= Context_CollectionChanged;
                }
            }
     
            public static void Transfer(IList source, IList target)
            {
                if (source == null || target == null)
                    return;
     
                target.Clear();
     
                foreach (var o in source)
                {
                    target.Add(o);
                }
            }
        }


    Regards,
    Lance | Tech Support Engineer, Sr.
    Telerik by Progress
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  3. Maurice
    Maurice avatar
    3 posts
    Member since:
    Apr 2016

    Posted 26 Jan Link to this post

    Thank you for your answer.

    I can't use x:bind because I have to target Build 10240.

    Also in the example of xaml behavior x:bind is used.

    I will start reading the documentation of xaml behavior if I can use this aproach.

     

    Kind regards,

    Maurice

  4. Maurice
    Maurice avatar
    3 posts
    Member since:
    Apr 2016

    Posted 26 Jan in reply to Maurice Link to this post

    Fixed it with:

    <interactivity:Interaction.Behaviors>
      <core:EventTriggerBehavior EventName="SelectionChanged">
       <core:InvokeCommandAction Command="{Binding SelectionChangedCommand}"  />
      </core:EventTriggerBehavior>
    </interactivity:Interaction.Behaviors>

     

    Kind regards,

    Maurice

     

  5. Answer
    Lance | Tech Support Engineer, Sr.
    Admin
    Lance | Tech Support Engineer, Sr. avatar
    457 posts

    Posted 26 Jan Link to this post

    Hello Maurice,

    Yes, the EventToCommand approach works, just be aware that it doesn't pass the event args automatically. You can either create a custom behavior like Normal does in this post or use a converter with your InvokeCommandAction like Iris does here.

    Once you have the args passed to the command, you'll be able to get e.AddedItems and e.RemovedItems to keep your VM's SelectedItems collection in sync.

    Regards,
    Lance | Tech Support Engineer, Sr.
    Telerik by Progress
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  6. Cody
    Cody avatar
    3 posts
    Member since:
    Sep 2017

    Posted 06 Oct in reply to Lance | Tech Support Engineer, Sr. Link to this post

    Woo lets bump a dinosaur thread... 

    Anyways I have implemented the multi-select behavior solution without thinking things through all the way... Is it at all possible to programatically select multiple items on a telerik RadDataGrid?

  7. Lance | Tech Support Engineer, Sr.
    Admin
    Lance | Tech Support Engineer, Sr. avatar
    457 posts

    Posted 10 Oct Link to this post

    Hello Cody,

    You can add or remove items from the DataGrid's SelectedItems property to accomplish this.

    Let's say I have a list of Students in the view model:

    public class ViewModel
    {
        public ViewModel()
        {
        }
     
        public ObservableCollection<Student> Students { get; set; } = new ObservableCollection<Student>();
    }


    that the DataGrid is bound to: 

    <grid:RadDataGrid x:Name="MyDataGrid" ItemsSource="{Binding Students}" />


    If I wanted to programmatically make the first item selected when the page loads, I would do this:

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        var vm = DataContext as ViewModel;
        MyDataGrid.SelectedItems.Add(vm?.Students.FirstOrDefault());
    }

    Let me know if you have any further questions.

    Regards,
    Lance | Tech Support Engineer, Sr.
    Progress Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
Back to Top