Bind to Selecteditems from Datagrid or Listbox in Mvvm

Bind to SelectedItems from DataGrid or ListBox in MVVM

This will work:

MultiSelectorBehaviours.vb

Imports System.Collections
Imports System.Windows
Imports System.Windows.Controls.Primitives
Imports System.Windows.Controls
Imports System

Public NotInheritable Class MultiSelectorBehaviours
Private Sub New()
End Sub

Public Shared ReadOnly SynchronizedSelectedItems As DependencyProperty = _
DependencyProperty.RegisterAttached("SynchronizedSelectedItems", GetType(IList), GetType(MultiSelectorBehaviours), New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf OnSynchronizedSelectedItemsChanged)))

Private Shared ReadOnly SynchronizationManagerProperty As DependencyProperty = DependencyProperty.RegisterAttached("SynchronizationManager", GetType(SynchronizationManager), GetType(MultiSelectorBehaviours), New PropertyMetadata(Nothing))

''' <summary>
''' Gets the synchronized selected items.
''' </summary>
''' <param name="dependencyObject">The dependency object.</param>
''' <returns>The list that is acting as the sync list.</returns>
Public Shared Function GetSynchronizedSelectedItems(ByVal dependencyObject As DependencyObject) As IList
Return DirectCast(dependencyObject.GetValue(SynchronizedSelectedItems), IList)
End Function

''' <summary>
''' Sets the synchronized selected items.
''' </summary>
''' <param name="dependencyObject">The dependency object.</param>
''' <param name="value">The value to be set as synchronized items.</param>
Public Shared Sub SetSynchronizedSelectedItems(ByVal dependencyObject As DependencyObject, ByVal value As IList)
dependencyObject.SetValue(SynchronizedSelectedItems, value)
End Sub

Private Shared Function GetSynchronizationManager(ByVal dependencyObject As DependencyObject) As SynchronizationManager
Return DirectCast(dependencyObject.GetValue(SynchronizationManagerProperty), SynchronizationManager)
End Function

Private Shared Sub SetSynchronizationManager(ByVal dependencyObject As DependencyObject, ByVal value As SynchronizationManager)
dependencyObject.SetValue(SynchronizationManagerProperty, value)
End Sub

Private Shared Sub OnSynchronizedSelectedItemsChanged(ByVal dependencyObject As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If e.OldValue IsNot Nothing Then
Dim synchronizer As SynchronizationManager = GetSynchronizationManager(dependencyObject)
synchronizer.StopSynchronizing()

SetSynchronizationManager(dependencyObject, Nothing)
End If

Dim list As IList = TryCast(e.NewValue, IList)
Dim selector As Selector = TryCast(dependencyObject, Selector)

' check that this property is an IList, and that it is being set on a ListBox
If list IsNot Nothing AndAlso selector IsNot Nothing Then
Dim synchronizer As SynchronizationManager = GetSynchronizationManager(dependencyObject)
If synchronizer Is Nothing Then
synchronizer = New SynchronizationManager(selector)
SetSynchronizationManager(dependencyObject, synchronizer)
End If

synchronizer.StartSynchronizingList()
End If
End Sub

''' <summary>
''' A synchronization manager.
''' </summary>
Private Class SynchronizationManager
Private ReadOnly _multiSelector As Selector
Private _synchronizer As TwoListSynchronizer

''' <summary>
''' Initializes a new instance of the <see cref="SynchronizationManager"/> class.
''' </summary>
''' <param name="selector">The selector.</param>
Friend Sub New(ByVal selector As Selector)
_multiSelector = selector
End Sub

''' <summary>
''' Starts synchronizing the list.
''' </summary>
Public Sub StartSynchronizingList()
Dim list As IList = GetSynchronizedSelectedItems(_multiSelector)

If list IsNot Nothing Then
_synchronizer = New TwoListSynchronizer(GetSelectedItemsCollection(_multiSelector), list)
_synchronizer.StartSynchronizing()
End If
End Sub

''' <summary>
''' Stops synchronizing the list.
''' </summary>
Public Sub StopSynchronizing()
_synchronizer.StopSynchronizing()
End Sub

Public Shared Function GetSelectedItemsCollection(ByVal selector As Selector) As IList
If TypeOf selector Is MultiSelector Then
Return TryCast(selector, MultiSelector).SelectedItems
ElseIf TypeOf selector Is ListBox Then
Return TryCast(selector, ListBox).SelectedItems
Else
Throw New InvalidOperationException("Target object has no SelectedItems property to bind.")
End If
End Function

End Class
End Class

IListItemConverter.vb

''' <summary>
''' Converts items in the Master list to Items in the target list, and back again.
''' </summary>
Public Interface IListItemConverter
''' <summary>
''' Converts the specified master list item.
''' </summary>
''' <param name="masterListItem">The master list item.</param>
''' <returns>The result of the conversion.</returns>
Function Convert(ByVal masterListItem As Object) As Object

''' <summary>
''' Converts the specified target list item.
''' </summary>
''' <param name="targetListItem">The target list item.</param>
''' <returns>The result of the conversion.</returns>
Function ConvertBack(ByVal targetListItem As Object) As Object
End Interface

TwoListSynchronizer.vb

Imports System.Collections
Imports System.Collections.Specialized
Imports System.Linq
Imports System.Windows

''' <summary>
''' Keeps two lists synchronized.
''' </summary>
Public Class TwoListSynchronizer
Implements IWeakEventListener

Private Shared ReadOnly DefaultConverter As IListItemConverter = New DoNothingListItemConverter()
Private ReadOnly _masterList As IList
Private ReadOnly _masterTargetConverter As IListItemConverter
Private ReadOnly _targetList As IList

''' <summary>
''' Initializes a new instance of the <see cref="TwoListSynchronizer"/> class.
''' </summary>
''' <param name="masterList">The master list.</param>
''' <param name="targetList">The target list.</param>
''' <param name="masterTargetConverter">The master-target converter.</param>
Public Sub New(ByVal masterList As IList, ByVal targetList As IList, ByVal masterTargetConverter As IListItemConverter)
_masterList = masterList
_targetList = targetList
_masterTargetConverter = masterTargetConverter
End Sub

''' <summary>
''' Initializes a new instance of the <see cref="TwoListSynchronizer"/> class.
''' </summary>
''' <param name="masterList">The master list.</param>
''' <param name="targetList">The target list.</param>
Public Sub New(ByVal masterList As IList, ByVal targetList As IList)
Me.New(masterList, targetList, DefaultConverter)
End Sub

Private Delegate Sub ChangeListAction(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))

''' <summary>
''' Starts synchronizing the lists.
''' </summary>
Public Sub StartSynchronizing()
ListenForChangeEvents(_masterList)
ListenForChangeEvents(_targetList)

' Update the Target list from the Master list
SetListValuesFromSource(_masterList, _targetList, AddressOf ConvertFromMasterToTarget)

' In some cases the target list might have its own view on which items should included:
' so update the master list from the target list
' (This is the case with a ListBox SelectedItems collection: only items from the ItemsSource can be included in SelectedItems)
If Not TargetAndMasterCollectionsAreEqual() Then
SetListValuesFromSource(_targetList, _masterList, AddressOf ConvertFromTargetToMaster)
End If
End Sub

''' <summary>
''' Stop synchronizing the lists.
''' </summary>
Public Sub StopSynchronizing()
StopListeningForChangeEvents(_masterList)
StopListeningForChangeEvents(_targetList)
End Sub

''' <summary>
''' Receives events from the centralized event manager.
''' </summary>
''' <param name="managerType">The type of the <see cref="T:System.Windows.WeakEventManager"/> calling this method.</param>
''' <param name="sender">Object that originated the event.</param>
''' <param name="e">Event data.</param>
''' <returns>
''' true if the listener handled the event. It is considered an error by the <see cref="T:System.Windows.WeakEventManager"/> handling in WPF to register a listener for an event that the listener does not handle. Regardless, the method should return false if it receives an event that it does not recognize or handle.
''' </returns>
Public Function ReceiveWeakEvent(ByVal managerType As Type, ByVal sender As Object, ByVal e As EventArgs) As Boolean Implements System.Windows.IWeakEventListener.ReceiveWeakEvent
HandleCollectionChanged(TryCast(sender, IList), TryCast(e, NotifyCollectionChangedEventArgs))

Return True
End Function

''' <summary>
''' Listens for change events on a list.
''' </summary>
''' <param name="list">The list to listen to.</param>
Protected Sub ListenForChangeEvents(ByVal list As IList)
If TypeOf list Is INotifyCollectionChanged Then
CollectionChangedEventManager.AddListener(TryCast(list, INotifyCollectionChanged), Me)
End If
End Sub

''' <summary>
''' Stops listening for change events.
''' </summary>
''' <param name="list">The list to stop listening to.</param>
Protected Sub StopListeningForChangeEvents(ByVal list As IList)
If TypeOf list Is INotifyCollectionChanged Then
CollectionChangedEventManager.RemoveListener(TryCast(list, INotifyCollectionChanged), Me)
End If
End Sub

Private Sub AddItems(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
Dim itemCount As Integer = e.NewItems.Count

For i As Integer = 0 To itemCount - 1
Dim insertionPoint As Integer = e.NewStartingIndex + i

If insertionPoint > list.Count Then
list.Add(converter(e.NewItems(i)))
Else
list.Insert(insertionPoint, converter(e.NewItems(i)))
End If
Next
End Sub

Private Function ConvertFromMasterToTarget(ByVal masterListItem As Object) As Object
Return If(_masterTargetConverter Is Nothing, masterListItem, _masterTargetConverter.Convert(masterListItem))
End Function

Private Function ConvertFromTargetToMaster(ByVal targetListItem As Object) As Object
Return If(_masterTargetConverter Is Nothing, targetListItem, _masterTargetConverter.ConvertBack(targetListItem))
End Function

Private Sub HandleCollectionChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
Dim sourceList As IList = TryCast(sender, IList)

Select Case e.Action
Case NotifyCollectionChangedAction.Add
PerformActionOnAllLists(AddressOf AddItems, sourceList, e)
Exit Select
Case NotifyCollectionChangedAction.Move
PerformActionOnAllLists(AddressOf MoveItems, sourceList, e)
Exit Select
Case NotifyCollectionChangedAction.Remove
PerformActionOnAllLists(AddressOf RemoveItems, sourceList, e)
Exit Select
Case NotifyCollectionChangedAction.Replace
PerformActionOnAllLists(AddressOf ReplaceItems, sourceList, e)
Exit Select
Case NotifyCollectionChangedAction.Reset
UpdateListsFromSource(TryCast(sender, IList))
Exit Select
Case Else
Exit Select
End Select
End Sub

Private Sub MoveItems(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
RemoveItems(list, e, converter)
AddItems(list, e, converter)
End Sub

Private Sub PerformActionOnAllLists(ByVal action As ChangeListAction, ByVal sourceList As IList, ByVal collectionChangedArgs As NotifyCollectionChangedEventArgs)
If sourceList Is _masterList Then
PerformActionOnList(_targetList, action, collectionChangedArgs, AddressOf ConvertFromMasterToTarget)
Else
PerformActionOnList(_masterList, action, collectionChangedArgs, AddressOf ConvertFromTargetToMaster)
End If
End Sub

Private Sub PerformActionOnList(ByVal list As IList, ByVal action As ChangeListAction, ByVal collectionChangedArgs As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
StopListeningForChangeEvents(list)
action(list, collectionChangedArgs, converter)
ListenForChangeEvents(list)
End Sub

Private Sub RemoveItems(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
Dim itemCount As Integer = e.OldItems.Count

' for the number of items being removed, remove the item from the Old Starting Index
' (this will cause following items to be shifted down to fill the hole).
For i As Integer = 0 To itemCount - 1
list.RemoveAt(e.OldStartingIndex)
Next
End Sub

Private Sub ReplaceItems(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
RemoveItems(list, e, converter)
AddItems(list, e, converter)
End Sub

Private Sub SetListValuesFromSource(ByVal sourceList As IList, ByVal targetList As IList, ByVal converter As Converter(Of Object, Object))
StopListeningForChangeEvents(targetList)

targetList.Clear()

For Each o As Object In sourceList
targetList.Add(converter(o))
Next

ListenForChangeEvents(targetList)
End Sub

Private Function TargetAndMasterCollectionsAreEqual() As Boolean
Return _masterList.Cast(Of Object)().SequenceEqual(_targetList.Cast(Of Object)().[Select](Function(item) ConvertFromTargetToMaster(item)))
End Function

''' <summary>
''' Makes sure that all synchronized lists have the same values as the source list.
''' </summary>
''' <param name="sourceList">The source list.</param>
Private Sub UpdateListsFromSource(ByVal sourceList As IList)
If sourceList Is _masterList Then
SetListValuesFromSource(_masterList, _targetList, AddressOf ConvertFromMasterToTarget)
Else
SetListValuesFromSource(_targetList, _masterList, AddressOf ConvertFromTargetToMaster)
End If
End Sub

''' <summary>
''' An implementation that does nothing in the conversions.
''' </summary>
Friend Class DoNothingListItemConverter
Implements IListItemConverter

''' <summary>
''' Converts the specified master list item.
''' </summary>
''' <param name="masterListItem">The master list item.</param>
''' <returns>The result of the conversion.</returns>
Public Function Convert(ByVal masterListItem As Object) As Object Implements IListItemConverter.Convert
Return masterListItem
End Function

''' <summary>
''' Converts the specified target list item.
''' </summary>
''' <param name="targetListItem">The target list item.</param>
''' <returns>The result of the conversion.</returns>
Public Function ConvertBack(ByVal targetListItem As Object) As Object Implements IListItemConverter.ConvertBack
Return targetListItem
End Function
End Class

End Class

Then for the XAML:

<DataGrid ..... local:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding SelectedResults}" />

And finally the VM:

Public ReadOnly Property SelectedResults As ObservableCollection(Of StatisticsResultModel)
Get
Return _objSelectedResults
End Get
End Property

Credit Goes to: http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html

How to support ListBox SelectedItems binding with MVVM in a navigable application

Try creating an IsSelected property on each of your data items and binding ListBoxItem.IsSelected to that property

<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>

Binding SelectedItems of ListView to ViewModel

1. One way to source binding:

You have to use SelectionChanged event. The easiest way is to write eventhandler in codebehind to "bind selecteditems" to viewmodel.

//ViewModel
public ICollectionView BusinessCollection {get; set;}
public List<YourBusinessItem> SelectedObject {get; set;}

//Codebehind
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var viewmodel = (ViewModel) DataContext;
viewmodel.SelectedItems = listview.SelectedItems
.Cast<YourBusinessItem>()
.ToList();
}

This still aligns with MVVM design, because view and viewmodel resposibilities are kept separated. You dont have any logic in codebehind and viewmodel is clean and testable.

2. Two way binding:

if you also need to update view, when viewmodel changes, you have to attach to ViewModel's PropertyChanged event and to the selected items' CollectionChanged event. of course you can do it in codebehind, but in this case I would create something more reusable:

//ViewModel
public ObservableCollection<YourBusinessItem> SelectedObject {get; set;}

//in codebehind:
var binder = new SelectedItemsBinder(listview, viewmodel.SelectedItems);
binder.Bind();

or can create custom attached property, so you can use binding syntax in xaml:

<ListView local:ListViewExtensions.SelectedValues="{Binding SelectedItem}" .../>
public class SelectedItemsBinder
{
private ListView _listView;
private IList _collection;

public SelectedItemsBinder(ListView listView, IList collection)
{
_listView = listView;
_collection = collection;

_listView.SelectedItems.Clear();

foreach (var item in _collection)
{
_listView.SelectedItems.Add(item);
}
}

public void Bind()
{
_listView.SelectionChanged += ListView_SelectionChanged;

if (_collection is INotifyCollectionChanged)
{
var observable = (INotifyCollectionChanged) _collection;
observable.CollectionChanged += Collection_CollectionChanged;
}
}

public void UnBind()
{
if (_listView != null)
_listView.SelectionChanged -= ListView_SelectionChanged;

if (_collection != null && _collection is INotifyCollectionChanged)
{
var observable = (INotifyCollectionChanged) _collection;
observable.CollectionChanged -= Collection_CollectionChanged;
}
}

private void Collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (var item in e.NewItems ?? new object[0])
{
if (!_listView.SelectedItems.Contains(item))
_listView.SelectedItems.Add(item);
}
foreach (var item in e.OldItems ?? new object[0])
{
_listView.SelectedItems.Remove(item);
}
}

private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems ?? new object[0])
{
if (!_collection.Contains(item))
_collection.Add(item);
}

foreach (var item in e.RemovedItems ?? new object[0])
{
_collection.Remove(item);
}
}
}

Attached property implementation

public class ListViewExtensions
{

private static SelectedItemsBinder GetSelectedValueBinder(DependencyObject obj)
{
return (SelectedItemsBinder)obj.GetValue(SelectedValueBinderProperty);
}

private static void SetSelectedValueBinder(DependencyObject obj, SelectedItemsBinder items)
{
obj.SetValue(SelectedValueBinderProperty, items);
}

private static readonly DependencyProperty SelectedValueBinderProperty = DependencyProperty.RegisterAttached("SelectedValueBinder", typeof(SelectedItemsBinder), typeof(ListViewExtensions));

public static readonly DependencyProperty SelectedValuesProperty = DependencyProperty.RegisterAttached("SelectedValues", typeof(IList), typeof(ListViewExtensions),
new FrameworkPropertyMetadata(null, OnSelectedValuesChanged));

private static void OnSelectedValuesChanged(DependencyObject o, DependencyPropertyChangedEventArgs value)
{
var oldBinder = GetSelectedValueBinder(o);
if (oldBinder != null)
oldBinder.UnBind();

SetSelectedValueBinder(o, new SelectedItemsBinder((ListView)o, (IList)value.NewValue));
GetSelectedValueBinder(o).Bind();
}

public static void SetSelectedValues(Selector elementName, IEnumerable value)
{
elementName.SetValue(SelectedValuesProperty, value);
}

public static IEnumerable GetSelectedValues(Selector elementName)
{
return (IEnumerable)elementName.GetValue(SelectedValuesProperty);
}
}

Binding a collection to SelectedItems in a ListBox without violating MVVM

If I recall correctly, at the conclusion of our last episode, we were using some whimsical WPF control that doesn't let you bind SelectedItems properly, so that's out. But if you can do it, it's by far the best way:

<NonWhimsicalListBox
ItemsSource="{Binding VNodes}"
SelectedItems="{Binding SelectedVNodes}"
/>

But if you're using System.Windows.Controls.ListBox, you have to write it yourself using an attached property, which is actually not so bad. There's a lot of code here, but it's almost entirely boilerplate (most of the C# code in this attached property was created by a VS IDE code snippet). Nice thing here is it's general and any random passerby can use it on any ListBox that's got anything in it.

public static class AttachedProperties
{
#region AttachedProperties.SelectedItems Attached Property
public static IList GetSelectedItems(ListBox obj)
{
return (IList)obj.GetValue(SelectedItemsProperty);
}

public static void SetSelectedItems(ListBox obj, IList value)
{
obj.SetValue(SelectedItemsProperty, value);
}

public static readonly DependencyProperty
SelectedItemsProperty =
DependencyProperty.RegisterAttached(
"SelectedItems",
typeof(IList),
typeof(AttachedProperties),
new PropertyMetadata(null,
SelectedItems_PropertyChanged));

private static void SelectedItems_PropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var lb = d as ListBox;
IList coll = e.NewValue as IList;

// If you want to go both ways and have changes to
// this collection reflected back into the listbox...
if (coll is INotifyCollectionChanged)
{
(coll as INotifyCollectionChanged)
.CollectionChanged += (s, e3) =>
{
// Haven't tested this branch -- good luck!
if (null != e3.OldItems)
foreach (var item in e3.OldItems)
lb.SelectedItems.Remove(item);
if (null != e3.NewItems)
foreach (var item in e3.NewItems)
lb.SelectedItems.Add(item);
};
}

if (null != coll)
{
if (coll.Count > 0)
{
// Minor problem here: This doesn't work for initializing a
// selection on control creation.
// When I get here, it's because I've initialized the selected
// items collection that I'm binding. But at that point, lb.Items
// isn't populated yet, so adding these items to lb.SelectedItems
// always fails.
// Haven't tested this otherwise -- good luck!
lb.SelectedItems.Clear();
foreach (var item in coll)
lb.SelectedItems.Add(item);
}

lb.SelectionChanged += (s, e2) =>
{
if (null != e2.RemovedItems)
foreach (var item in e2.RemovedItems)
coll.Remove(item);
if (null != e2.AddedItems)
foreach (var item in e2.AddedItems)
coll.Add(item);
};
}
}
#endregion AttachedProperties.SelectedItems Attached Property
}

Assuming AttachedProperties is defined in whatever the "local:" namespace is in your XAML...

<ListBox 
ItemsSource="{Binding VNodes}"
SelectionMode="Extended"
local:AttachedProperties.SelectedItems="{Binding SelectedVNodes}"
/>

ViewModel:

private ObservableCollection<Node> _selectedVNodes 
= new ObservableCollection<Node>();
public ObservableCollection<Node> SelectedVNodes
{
get
{
return _selectedVNodes;
}
}

If you don't want to go there, I can think of threethree and a half straightforward ways of doing this offhand:

  1. When the parent viewmodel creates a VNode, it adds a handler to the new VNode's PropertyChanged event. In the handler, it adds/removes sender from SelectedVNodes according to (bool)e.NewValue

    var newvnode = new VNode();
    newvnode.PropertyChanged += (s,e) => {
    if (e.PropertyName == "IsSelected") {
    if ((bool)e.NewValue) {
    // If not in SelectedVNodes, add it.
    } else {
    // If in SelectedVNodes, remove it.
    }
    }
    };

    // blah blah blah
  2. Do that event, but instead of adding/removing, just recreate SelectedVNodes:

    var newvnode = new VNode();
    newvnode.PropertyChanged += (s,e) => {
    if (e.PropertyName == "IsSelected") {
    // Make sure OnPropertyChanged("SelectedVNodes") is happening!
    SelectedVNodes = new ObservableCollection<VNode>(
    VNodes.Where(vn => vn.IsSelected)
    );
    }
    };
  3. Do that event, but don't make SelectedVNodes Observable at all:

    var newvnode = new VNode();
    newvnode.PropertyChanged += (s,e) => {
    if (e.PropertyName == "IsSelected") {
    OnPropertyChanged("SelectedVNodes");
    }
    };

    // blah blah blah much else blah blah

    public IEnumerable<VNode> SelectedVNodes {
    get { return VNodes.Where(vn => vn.IsSelected); }
    }
  4. Give VNode a Parent property. When the parent viewmodel creates a VNode, it gives each VNode a Parent reference to the owner of SelectedVNodes (presumably itself). In VNode.IsSelected.set, the VNode does the add or remove on Parent.SelectedVNodes.

    //  In class VNode
    private bool _isSelected = false;
    public bool IsSelected {
    get { return _isSelected; }
    set {
    _isSelected = value;
    OnPropertyChanged("IsSelected");
    // Elided: much boilerplate checking for redundancy, null parent, etc.
    if (IsSelected)
    Parent.SelectedVNodes.Add(this);
    else
    Parent.SelectedVNodes.Remove(this);
    }
    }

None of the above is a work of art. Version 1 is least bad maybe.

Don't use the IEnumerable one if you've got a very large number of items. On the other hand, it relieves you of the responsibility to make this two-way, i.e. if some consumer messes with SelectedVNodes directly, you should really be handling its CollectionChanged event and updating the VNodes in question. Of course then you have to make sure you don't accidentally recurse: Don't add one to the collection that's already there, and don't set vn.IsSelected = true if vn.IsSelected is true already. If your eyes are glazing over like mine right now and you're starting to feel the walls closing in, allow me to recommend option #3.

Maybe SelectedVNodes should publicly expose ReadOnlyObservableCollection<VNode>, to get you off that hook.



Related Topics



Leave a reply



Submit