Multiple ListBox / Collection items selected for MVVM
I have a list associated with the ObservableCollection of Names. Some of the list items will have a checkbox to be enabled / disabled, indicating that the item has been selected.
How do I create an ObservableCollection from the selected items of the first list according to the Master-Details concept?
(I plan on using my MasterViewModel as the DataContext for my DetailsView, which displays the selected Collection items.)
Thanks in advance!
source to share
Yes, I've come across this before. ListBox, etc. Have a dependency property called "SelectedItem", but the "SelectedItems" property (with "s") is not implemented as one.
The cleanest solution I have found is to simply subclass the list and create your own dependency property called "SelectedItems". Not fun, but I think this is the best solution.
UPDATE
First, our ViewModel:
class ViewModel : INotifyPropertyChanged
{
// Set up our collection to be read from the View
public ObservableCollection<String> Collection { get; private set; }
// This collection will maintain the selected items
public ObservableCollection<String> SelectedItems { get; private set; }
public ViewModel()
{
// Instantiate
this.Collection = new ObservableCollection<String>();
this.SelectedItems = new ObservableCollection<String>();
// Now let monitor when this.SelectdItems changes
this.SelectedItems.CollectionChanged += SelectedItems_CollectionChanged;
// Fill our collection with some strings (1 to 10).
// (1) Generate the numbers 1 - 10
// (2) Convert each number to a string
// (3) Cast into a list so we can use foreach
// (4) Add each item to the collection.
Enumerable.Range(1, 10)
.Select(number => number.ToString())
.ToList()
.ForEach(this.Collection.Add);
// Remember! Never reset the ObservableCollection.
// That is, never say this.Collection = new... (or you'll break the binding).
// instead use this.Collection.Clear(), and then add the items you want to add
}
void SelectedItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (String str in this.SelectedItems)
System.Diagnostics.Debug.WriteLine("New item added {0}", str);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then our expanded ListBoxEx:
class ListBoxEx : ListBox
{
// Use the 'new' keyword so that we 'hide' the base property.
// This means that binding will go to this version of SelectedItems
// rather than whatever the base class uses. To reach the base 'SelectedItems' property
// We just need to use base.SelectedItems instead of this.SelectedItems
// Note that we register as an observable collection.
new DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof(ObservableCollection<String>), typeof(ListBoxEx));
// Accessor. Again, note the 'new'.
new public ObservableCollection<String> SelectedItems
{
get { return (ObservableCollection<String>) GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
// Guard against ViewModel being null
if (this.SelectedItems != null)
{
// Clear the list
this.SelectedItems.Clear();
// (1) On selection changed. Get the new base.SelectedItems
// (2) Cast each item to a String ("Make a string collection")
// (3) Cast to list, and use foreach to add each item to
// this.SelectedItems (note this is different from the original base.SelectedItems)
base.SelectedItems.Cast<String>()
.ToList()
.ForEach(this.SelectedItems.Add);
}
}
}
And finally, our view:
<Window.DataContext>
<lol:ViewModel />
</Window.DataContext>
<Grid>
<lol:ListBoxEx ItemsSource="{Binding Collection}" SelectedItems="{Binding SelectedItems}"
SelectionMode="Multiple"/>
</Grid>
source to share