Thanks,
Chris
9 Answers, 1 is accepted
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
George
the Telerik team
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!
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();
}
}
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;
}
});
}
}
}
public
Action<int> OnSelectFirstDocumentType;
or
public
Action<object> OnSelectFirstDocumentType;
and send index or item to select (and select it in handler in view).
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.
++
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);
}
}
}
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.