This is a migrated thread and some comments may be shown as answers.

RadListBox SelectedItems MVVM

9 Answers 750 Views
ListBox
This is a migrated thread and some comments may be shown as answers.
Chris
Top achievements
Rank 2
Chris asked on 20 Feb 2012, 09:21 PM
I can bind a ListBox ItemsSource to an ObservableCollection<T> and it works well.  But I need to allow the user to select n number of items in the list.  The list box supports this with SelectionMode="Multiple".  I figured if I bound the SelectedItem to a ObservableCollection<T> I'd be able to use it back in my ViewModel, but this is not working.  How do I bind the selected items from the ListBox to my ViewModel and have access to the selected items in the VM?

Thanks,
Chris

9 Answers, 1 is accepted

Sort by
0
AGM
Top achievements
Rank 1
answered on 21 Feb 2012, 09:12 AM
Exactly, what i want to know!
0
George
Telerik team
answered on 23 Feb 2012, 04:37 PM
Hello,

 

Currently the RadListBox control doesn't provide any way to manipulate the selected items in a MVVM friendly scenario. What I could suggest is to use the SelectionChanged property and in the handler you can get the RadListBox.SelectedItems property (it has only a getter and no setter). We consider this as a feature request and we logged it in our backlog system. You could vote for it and observe its progress here -
http://www.telerik.com/support/pits.aspx#/public/silverlight/9902 
 

All the best,
George
the Telerik team
Sharpen your .NET Ninja skills! Attend Q1 webinar week and get a chance to win a license! Book your seat now >>
0
Chris
Top achievements
Rank 2
answered on 23 Feb 2012, 09:08 PM

I do have a similar work around, I was just hoping the control had the feature and I was missing it.  That I did was attached a trigger, and using MVVMLight's EventToCommand mapped that to my ViewModel.  in the ViewModel I assign the selected items to a collection...

My trigger (on the listbox):

<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding MethodChangedCommand}" CommandParameter="{Binding SelectedItems, ElementName=callMethodListBox}" />
</i:EventTrigger>
</i:Interaction.Triggers>

Then in my ViewModel:
MethodChangedCommand = new RelayCommand<IList>(
items =>
{
_selectedCallMethods.Clear();
foreach (var item in items)
_selectedCallMethods.Add(item as NameValueViewModel<int>);
});


I'm still pretty new to MVVM, and there is probably a more elegant way to accomplish this, if others have a better way please feel free to share!
0
AvgurD
Top achievements
Rank 1
answered on 02 May 2012, 10:09 AM
Hello!

I found one solution for myself - but it's not "pure" MVVM.

In VM:

public Action OnSelectFirstDocumentType;
 
private void LoadDocumentTypesComleted(LoadOperation<DocumentType> obj)
{
    ...
    ...load content of listbox
    ...
    if (DocumentTypes.Count > 0)
    {
        OnSelectFirstDocumentType();
    }
}
In View:
public partial class AddEditDocumentView
   {
       public AddEditDocumentView()
       {
           InitializeComponent();
           this.Loaded += new System.Windows.RoutedEventHandler(AddEditDocumentView_Loaded);
       }
 
       void AddEditDocumentView_Loaded(object sender, System.Windows.RoutedEventArgs e)
       {
           //handler - select first item in type's listbox
           if (this.DataContext as AddEditDocumentViewModel != null)
           {
               (this.DataContext as AddEditDocumentViewModel).OnSelectFirstDocumentType = (() => if (DocTypeListBox.Items.Count > 0)
                                                                                              {
                                                                                                   DocTypeListBox.SelectedIndex = 0;                                                                                                  
                                                                                              }
                                                                                          });
           }
       }
   }
0
AvgurD
Top achievements
Rank 1
answered on 02 May 2012, 10:18 AM
If you want to select custom item - you must create event like this

public Action<int> OnSelectFirstDocumentType; 
or 
public Action<object> OnSelectFirstDocumentType; 

and send index or item to select (and select it in handler in view).
0
lnu
Top achievements
Rank 1
answered on 01 Jun 2012, 02:37 PM
Hi,

I'm using an attached property for this scenario:

using System.Collections;
using System.Windows;
using Telerik.Windows.Controls;
 
namespace test
{
    /// <summary>
    /// This class allow to bind selected Items from ListBox.
    /// </summary>
    public static class SelectedItemsHelperRadListBox
    {
        /// <summary>
        /// SelectedItems Attached Dependency Property.
        /// </summary>
        public static readonly DependencyProperty SelectedItemsProperty =
            DependencyProperty.RegisterAttached("MySelectedItems", typeof(IList), typeof(SelectedItemsHelperRadListBox), new FrameworkPropertyMetadata((IList)null, new PropertyChangedCallback(OnSelectedItemsChanged)));
 
        /// <summary>
        /// Gets my selected items.
        /// </summary>
        /// <param name="d">The d.</param>
        /// <returns></returns>
        public static IList GetMySelectedItems(DependencyObject d)
        {
            return (IList)d.GetValue(SelectedItemsProperty);
        }
 
        /// <summary>
        /// Sets my selected items.
        /// </summary>
        /// <param name="d">The d.</param>
        /// <param name="value">The value.</param>
        public static void SetMySelectedItems(DependencyObject d, IList value)
        {
            d.SetValue(SelectedItemsProperty, value);
        }
 
        /// <summary>
        /// Called when [selected items changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnSelectedItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var listBox = sender as RadListBox;
            if (listBox != null)
            {
                ReSetSelectedItems(listBox);
                listBox.SelectionChanged += delegate
                {
                    ReSetSelectedItems(listBox);
                };
            }
        }
 
        /// <summary>
        /// Res the set selected items.
        /// </summary>
        /// <param name="listBox">The list box.</param>
        private static void ReSetSelectedItems(RadListBox listBox)
        {
            IList selectedItems = GetMySelectedItems(listBox);
            if (selectedItems != null)
            {
                selectedItems.Clear();
                if (listBox.SelectedItems != null)
                {
                    foreach (var item in listBox.SelectedItems)
                    {
                        selectedItems.Add(item);
                    }
                }
            }
        }
    }

<telerik:RadListBox x:Name="xxx"
                                        Grid.Row="1"
                                        Helper:SelectedItemsHelperRadListBox.MySelectedItems="{Binding SelectedDatas}"
                                        ItemsSource="{Binding Datas}"
                                        SelectionMode="Extended"
                                        Margin="0,5,0,0">
</<telerik:RadListBox>

It does the job for me.

++
0
Erik
Top achievements
Rank 1
answered on 18 Aug 2012, 01:01 PM
I modified the solution described here:
http://blogs.telerik.com/vladimirenchev/posts/10-05-31/how-to-synchronize-your-ui-selected-items-with-your-data-context-using-mvvm-and-blend-behaviors-for-silverlight-and-wpf.aspx
to work with the RadListBox:

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

0
Alfons
Top achievements
Rank 2
answered on 31 Jan 2013, 10:36 AM
All the mentioned solutions above didn't fit my needs (I think): I had to be able to set the SelectedItems as well.

I have a collection in an object and I want to edit my object in a RadDataForm.

This is how I solved it:
1. I added two DependencyProperties to my class derived from DataFormDataField: Population (all selectable items) and SelectedItems (which was bound to THE collection).
2. Since the items of a RadListBox cannot be selected programmatically (correct me if I'm wrong), I created an ItemTemplate with a checkbox.
3. The ListBox.ItemsSource I bound to my Population (via a MultiValueConverter).
4. In this converter I combined both the DependencyProperties and created a List of objects with a Selected property (which was bound to the IsChecked property of the checkbox).
5. To the checkbox I added two handlers for the Checked and Unchecked event. In here I added and removed items to and from the SelectedItems property.

That's it. I hope this is useful to someone.
0
Adrian
Top achievements
Rank 1
answered on 21 Feb 2013, 12:06 PM
To delete
Tags
ListBox
Asked by
Chris
Top achievements
Rank 2
Answers by
AGM
Top achievements
Rank 1
George
Telerik team
Chris
Top achievements
Rank 2
AvgurD
Top achievements
Rank 1
lnu
Top achievements
Rank 1
Erik
Top achievements
Rank 1
Alfons
Top achievements
Rank 2
Adrian
Top achievements
Rank 1
Share this question
or