Pages

Thursday, 12 February 2009

How to Databind to a SelectedItems property in WPF

Many moons ago, I asked on the WPF forums if anybody had a way of data-binding the SelectedItems property of a ListBox. Standard data binding doesn’t work, because the SelectedItems property is read-only, and understandably so: how would you like it if I injected an arbitrary collection into your ListBox and expected you to keep it up to date as the selection changed?

As no one gave me an answer, I was forced to use my gray-matter and invent a solution of my own. Since a similar question came up again recently on Stack Overflow, I thought I’d share what I came up with.

In my solution, I define an attached property that you attach to a ListBox (or DataGrid, or anything that inherits from MultiSelector) and allows you to specify a collection (via data binding, of course) that you want to be kept in sync with the SelectedItems collection of the target. To work properly, the collection you give should implement INotifyCollectionChanged – using ObservableCollection<T> should do the trick. When you set the property, I instantiate another class of my invention, a TwoListSynchronizer. This listens to CollectionChanged events in both collections, and when either changes, it updates the other.

To help me demonstrate, I’ve knocked up a toy ViewModel: it has on it an AvailableNames property that will be the data source for the ListBox, a SelectedNames property which is the reason we’re going through the whole exercise, and a Summary property which displays the number of selected items to prove that the data binding is working correctly.  I won’t bore you with the code – see the download link below.

The XAML looks like this:

<Window x:Class="SelectedItemsBindingDemo.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="clr-namespace:SelectedItemsBindingDemo"
  xmlns:ff="clr-namespace:FunctionalFun.UI.Behaviours"
  Title="SelectedItems Binding Demo" Height="300" Width="300"
      >
<Window.DataContext>
  <d:ViewModel/>
</Window.DataContext>
  <Grid Margin="5">
    <Grid.RowDefinitions>
      <RowDefinition Height="*"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Center"
               Text="{Binding Summary}" FontWeight="Bold" Margin ="0,5,5,5"/>
    <Button Content="Select All" Command="{Binding SelectAll}" Grid.Row="1"
            HorizontalAlignment="Right" VerticalAlignment="Center"/>
    <ListBox Grid.Row="0"
             ItemsSource="{Binding AvailableNames}"
             ff:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding SelectedNames}"
             SelectionMode="Extended" />
  </Grid>
</Window>

All the magic is initiated by line 22 where I set the attached property MultiSelectorBehaviours.SynchronizedSelectedItems using a data binding to my SelectedNames property. With that in place the SelectedNames collection in my ViewModel is updated whenever SelectedItems on the ListBox changes. And to prove it works the other way to, I’ve created a SelectAll command on the ViewModel that puts all available names into the SelectedNames collection. When you press the button you’ll see that the ListBox obediently updates to show all items selected.

The TwoListSynchronizer code does not make especially thrilling reading, so I won’t show it here. If you should peruse it however, there are a couple of things you might wonder about:

  • I’ve made it implement IWeakEventListener so that it can subscribe to CollectionChanged events using weak events. This means I don’t have to worry about creating managed memory leaks if the source SelectedItems collection has a longer lifetime than the ListBox.
  • The code occasionally refers to something called a IListItemConverter. I’m not make use of this in this application of the TwoListSynchronizer, but it might have uses elsewhere. The scenario I had in mind was where there is some kind of mapping between the items in the two collections that need to be synchronized.

Update (25/07/2013): All the code is now available on Github (under a Public Domain License). Download a zip file directly here.

88 comments:

  1. Great article. I got it to work successfully, but only if I start with no selection. If I already have selected items at load time, the SynchronizedSelectedItems attached property seems to get bound before the ItemsSource, so it clears the selected items.

    Is there a way to ensure the ItemsSource has populated the list before the Synchronization kicks in?

    ReplyDelete
  2. I think I got it working now and might have found a bug. If I swap _targetList and _masterList in ListSynchronizer.SetListValuesFromSourceTwo, things work for me.

    ReplyDelete
  3. Anthony,
    Thanks for letting me know about this. I'll look into it when I get a chance.

    ReplyDelete
  4. Thanks for the solution. It works well. I had the same issue Anthony wrote about. I applied his change and it appears to be behaving the way I expect.

    ReplyDelete
  5. me here, it works when I swap the parameters.

    Great snippet though, thanks.

    ReplyDelete
  6. This is pure gold. Thank you!

    ReplyDelete
  7. This really made my day! Thanks a lot!!!

    ReplyDelete
  8. Great stuff!

    One thing: I'd like to enhance this thing to be aware of sort of "reinstanciation" of the source list:

    List = new ObservableCollection<string>();

    where List is a DependencyProperty of a superior object.

    The listeners must somehow be made aware of this new list instance. Any idea?

    ReplyDelete
  9. Ah, as I've just seen, it is aware of a new list instance. But for some reason the new list instance gets fed with the values of the old list instead of setting the target (a ListBox in this case) to its initial values.

    ReplyDelete
  10. Arr... okay, it's the same issue Anthony Brien mentioned some months ago. I swapped sourceList and targetList in the TwoListSynchronizer.SetListValuesFromSource method and now it works as supposed. Programmatic changes to the bound collection are correctly propagated to the SelectedItems collection of the bound ListBox.

    ReplyDelete
  11. Does this work with a multi column ListView?

    ReplyDelete
  12. @Anonymous: I can see no reason why it wouldn't work with ListView - ListView inherits from ListBox, and the multi-column nature of ListView doens't affect the way selection works.

    @Hendrik: Glad you're finding this useful. When I get a moment, I'll try to update the code with Anthony's bug fix.

    ReplyDelete
  13. When using it with a ListView, my binding to the multi column ListView is an ObservableCollection for CustomDataClass instead of the ObservableCollection for string I used with a ListBox. When I select a row, the converter throws an exception because it cannot convert CustomDataClass to string.

    ReplyDelete
  14. @Anonymous: so your data source (bound to the ItemsSource property on the ListView) is of type ObservableCollection<CustomDataClass>, correct? Have you made sure that the property you're binding to SynchronizedSelectedItems is of the same type? If you have and it's still not working, feel free to contact me at samuel.jack@gmail.com with more details, and I'll take a look.

    ReplyDelete
  15. Trying this great snippet with Visual Studio 4 Beta1 I'll get following exception in designer:

    'Cannot create an instance of "IList".'
    C:\Users\...\Window1.xaml 20 7 SelectedItemsBindingDemo

    Compiled program works.

    Bye Stefan

    ReplyDelete
  16. Thanks Sam, this is great stuff! Really helped me a lot!

    ReplyDelete
  17. Is there a license attached to this code?

    ReplyDelete
  18. All the code on this site is licensed under Creative Commons Attribution 2.0 - as shown on the sidebar ;-).

    ReplyDelete
  19. Hi,

    Excellent piece of code, this is massively useful for MVVM!

    I am trying to pre-populate my SelectedItems in the constructor of the ViewModel but this is not reflected in the SelectedItems property. What do I need to change?

    Thanks,

    Daniel

    ReplyDelete
  20. Daniel,
    I think that might be a bug in my code. See Anthony's comment above - try swapping _targetList and _masterList in ListSynchronizer.SetListValuesFromSourceTwo

    ReplyDelete
  21. thanks for this code, very helpful when doing MVVM.

    ReplyDelete
  22. thx, you saved my day. i m not that good with .net yet, trying to get into mvvm and you solved the hell of a problem for me.

    ReplyDelete
  23. I am sorry to bother you with this but I am unable to find the "SetListValuesFromSourceTwo" method everyone is talking about in the source code to fix the issue with MVVM and setting up the selected items before binding. What am I missing? I used this link http://cid-7ae21202966f24d8.skydrive.live.com/self.aspx/Public/Blog/SelectedItemsBindingDemo.zip and that method name certainly does not exist in any of that code.

    ReplyDelete
  24. Todd,
    I think the "Two" belongs with the class name rather than the method name - it should be TwoListSynchronizer.SetListValuesFromSource.

    But I think I've found a better place for the bug fix anyway: in line 103 of the MultiSelectorBehaviors file, swap the parameters to the TwoListSynchronizer constructor.

    ReplyDelete
  25. Hi,

    Thanks for this code too. Sorry for the long comment - I have a query and a cautionary tale.

    i've found that if you change the datacontext of a parent control then the listbox control will clear all it's selected items. Since the weak events are still attached at this point your target list will be cleared too. Wasnt what i wanted so i remove these event listeners in a data context changed event handler for the control (added to MultiSelectorBehaviour). Not a bug in your code (unexpected behaviour in the framework) but the consequences of this when databound can be higher.

    The query is related to the StartSynchronizing method - you check TargetAndMasterCollectionsAreEqual() and potentially synchronise in the opposite direction. The comment above this call mentions "(This is the case with a ListBox SelectedItems collection: only items from the ItemsSource can be included in SelectedItems)" - but i don't follow. Can this case only ever arise if the ConvertFromMasterToTarget fails for an item ?

    ReplyDelete
  26. Yes, I've seen that behaviour, but I don't have a proven general-purpose solution unfortunately.

    The comment you refer to: what this means is that if you try to add an item to the ListBox SeleectedItems collection that isn't in the ListBox ItemsSource collection, the item won't get added. You might get into this situation if you cached the previous list of SelectedItems, but meanwhile the ItemsSource collection has changed. So I synchronise back in the other direction to make sure there are no erroneous items in SelectedItems. If you always pre-filter your SelectedItems collection then you can safely remove that call.

    ReplyDelete
  27. je ne sais pas beaucoup l'anglais , alors,
    je peut ecrire en français mon problème?

    ReplyDelete
  28. Je connais probablement encore moins français. Essayez http://translate.google.com

    ReplyDelete
  29. Thanks for this brilliant and usefull behaviour !

    ReplyDelete
  30. Hi Sam,
    before i read your execllent article, i find my stupid solution. just like this

    in file.xaml



    in file.cs

    public partial class MyControl: UserControl
    {
    public MyControl()
    {
    InitializeComponent();
    DataContextChanged += OnDataContextChanged;
    }

    private void checkedListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
    ((ISelectionChanged) DataContext).SelectedItems = OpCheckedListView.SelectedItems;
    }
    }

    in viewModel.cs

    public IList SelectedItems
    {
    get { return _selectedItems; }
    set
    {
    if (_selectedItems != value)
    {
    _selectedItems = value;
    OnPropertyChanged("SelectedItems");
    }
    }
    }
    {
    ((ISelectionChanged) DataContext).SelectedItems = OpCheckedListView.SelectedItems;
    }

    this run, but this is not generic, i think.
    can you give me some suggestion? or improve this piece of code?
    thanks

    shaofeng

    shaofeng.zhu@hotmail.fr

    ReplyDelete
  31. i just forget this code in file.xaml


    shaofeng

    shaofeng.zhu@hotmail.fr

    ReplyDelete
  32. i just forget this code in file.xaml

    ListView x:Name="OpCheckedListView"
    SelectionMode="Multiple" SelectionChanged="checkedListView_SelectionChanged"
    ItemsSource="{Binding AList}"

    ReplyDelete
  33. Thanks for taking the effort to work it out.
    Nice solution!

    ReplyDelete
  34. Thanks sam for this nice solution it works perfect!

    Nelly

    ReplyDelete
  35. Will this work in Silverlight?

    ReplyDelete
  36. @Anonymous,
    It can be made to work in Silverlight, but it's not a copy-and-paste job unfortunately.

    If I get time, I'll post my Silverlight version later.

    ReplyDelete
  37. I'm new to the whole MVVM concept but I am having a hard time finding where your code actually changes the values in _selectedNames. I would have assumed there was a set command for SelectAll but there is not. How do you actually set new values of _selectedNames.
    thanks for the code and the help

    ReplyDelete
  38. I presume you've downloaded the code? If you look in ViewModel.cs at the SelectAll property, you see that I change the selected names by adding and removing items from the SelectedNames collection. The magic is that this is then reflected automatically in the items that are selected in the ListBox.

    Does that help?

    ReplyDelete
  39. I have downloaded the code. I am still trying to figure out how the list is updated if I manually select a single item in the list. When I put a break point in the SelectAll command it does not stop there.

    ReplyDelete
  40. @pyrahna, To see what's happening, you'd need to point a breakpoint in TwoListSynchronizer.HandleCollectionChanged - that's where changes made to the one list are propagated to the other.

    ReplyDelete
  41. It appears that my ReceiveWeakEvent isn't being thrown. Any idea why that might be?

    ReplyDelete
  42. Very useful!
    I thought it wasn't enough at first sight because of the reported and fixed bug, then I deleted it, and downloaded it again after having read the comments =)
    You really have to update the archive :D

    ReplyDelete
  43. @N3tm4n,
    Thanks for pointing out the question. I'm afraid I don't have a good solution to it at the moment, but I'll give it some thought.

    ReplyDelete
  44. Hello Sam,

    in the whole project there exists nothing like:
    ListSynchronizer.SetListValuesFromSourceTwo

    I have the same bug the others experience. Would you please show me where exactly to change this?

    ReplyDelete
  45. You can see the location and other amenities of the condo. This will give idea how is it like living in a Miami condo.
    Miami MLS

    ReplyDelete
  46. Hello,
    Has any one done this twoway binding in Silverlight ? it really givesme such a pain!

    ReplyDelete
  47. To save anyone else time, you can absolutely bind to ListBox's selected item: http://msdn.microsoft.com/en-us/magazine/cc163299.aspx

    You cannot to a ListView without a workaround: http://social.msdn.microsoft.com/Forums/en-us/wpf/thread/edd335ea-e5e1-48e1-91a2-793d613f5cc3

    ReplyDelete
  48. I have downloaded the code and discovered a weird behavior. If you add a second list box under the first, , and select some items on the first listbox and then select something on the second list box everything appears to disappear in the 1st listbox. Those items are still selected in the VM but the are not visible in the view. Does anyone know how to address this?

    ReplyDelete
  49. You should upload this to GitHub for easy patching/forking :)

    ReplyDelete
  50. @Kamran,
    I've certainly got that on my todo list (I was also thinking of making a NuGet of it).

    ReplyDelete
  51. Hello Sam,

    please make a NuGet of it and update the code, because the download link does NOT work. I selected 5 items and 2 items get deleted - it seems randomly...-

    thanks in advance, bastien

    ReplyDelete
  52. Does any body used this in Silverlight.. I need Siolverlight version of this code.

    ReplyDelete
  53. Where's code ? The zip i obtain is 0 octet...

    ReplyDelete
  54. Hi Sam, applied your fix to the MultiSelectorBehaviours.cs file and it worked like a charm. Very elegant solution, cheers!

    ReplyDelete
  55. Hi sam, I try to upload your application , but this not download, this application is seem very useful, How do I download this application, please?Very Thanks

    ReplyDelete
  56. Has anyone gotten this to work with a binding validation? Everything binds perfectly, but the object I'm binding to I use for doing data validation. In my binding, I've got:
    morestuf:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding The_selectedVersions, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}":MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding The_selectedVersions, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}"
    But it doesn't seem to work. The way that I currently got the validation to work is to attach an event handler to the ListBox's SelectionChanged event, and the following code inside:

    Dim theExpression = VersionsLB.GetBindingExpression(MultiSelect.MultiSelectorBehaviours.SynchronizedSelectedItems)

            theExpression.UpdateTarget()
            theExpression.UpdateTarget()

    With this in place, everything works perfectly, but it would be nice not to have to wire up an event handler. Does code need to be included in the MultiSelect that checks to see if ValidatesOnDataErrors is set, and act on it? This seems to happen automatically when you bind in a regular scenario.

    Any thoughts would be greatly appreciated!

    -Ben

    ReplyDelete
  57. Ben,
       Setting ValidatesOnDataErrors=True in the binding causes validation to happen when the value of the property changes. But in typical usage the value of the SynchronizedSelectedItems property only changes when the control is first loaded and a collection is assigned to it. When the selection in the ListBox changes, it is the collection contents which change, and the data binding system is unaware of that.

    I can't think of anything you could do my behavior code which would help here, because validation happens at the level of the properties on the ViewModel.

    An alternative to your fix that you might try (assuming you're using the Model-View-ViewModel pattern): in your ViewModel, add an event handler to the CollectionChanged event of your SelectedItems collection  (the one you're binding to the ListBox, exposed in the property The_selectedVersions). In that event handler, raise a PropertyChanged event for the The_selectedVersions property.

    ReplyDelete
  58. Hey Sam,
     
    First, thanks for replying!
     
    I think your solution would work well. (I am indeed trying to implement the MVVM patern.)

    I successfully added an event handler to the_selectedVersions.CollectionChanged, and in that event handler, I tried to raise the PropertyChanged event.

    But I've got a question: As events must be raised from inside the owning class, how could I raise a PropertyChanged event on the The_selectedVersions ObservableCollection? I also tried calling the method OnPropertyChanged, but not surprisingly, I got:Error 1 'System.Collections.ObjectModel.ObservableCollection(Of T).Protected Overridable Sub OnPropertyChanged(e As System.ComponentModel.PropertyChangedEventArgs)' is not accessible in this context because it is 'Protected'.I'm wondering if there's something that I misunderstood?Thanks Sam...-Ben
    The_selectedVersions ObservableCollection? I also tried calling the method OnPropertyChanged, but not surprisingly, I got:

    Error 1 'System.Collections.ObjectModel.ObservableCollection(Of T).Protected Overridable Sub OnPropertyChanged(e As System.ComponentModel.PropertyChangedEventArgs)' is not accessible in this context because it is 'Protected'.

    I'm wondering if there's something that I misunderstood?

    Thanks Sam...

    -Ben

    ReplyDelete
  59. Ben, you don't need to raise the PropertyChanged event from the ObservableCollection, but from the ViewModel, with the property name being the property that holds the observable collection.

    Here's a small example:

    public class MyViewModel : ViewModelBase
    {
       public ObservableCollection SelectedItems {get; private set;}
      
       public MyViewModel() 
       {
          SelectedItems = new ObservableCollection();
          SelectedItems.CollectionChanged += delegate { RaisePropertyChanged("SelectedItems"); };
       }

       ...
    }

    ReplyDelete
  60. Sam, that's fantastic. With your example, I was able to get it to work! Now my view is nice and clean :)
    -Ben

    ReplyDelete
  61. Excellent - glad I could help.

    ReplyDelete
  62. I discovered your web site via Google while looking for a related subject, lucky for me your web site came up, it seems to be great. I have bookmarked it in my google bookmarks.

    ReplyDelete
  63. I discovered your web site via Google while looking for a related subject, lucky for me your web site came up, it seems to be great. I have bookmarked it in my google bookmarks.

    ReplyDelete
  64. (I hope my english will be eas for reader to understand, i appologize for incoming eyes issues you may encounter)

    Scenario : 
    We're using this behavior in an application using MVVM, onto a ListBox. Nothing special.
    We're loading the ViewModel First, then the View
    we have something like : 


     

        Public Property SelectedItems() As ObservableCollection(Of Item)

    Public MustInherit Class ViewModelBase
        Public MustOverride Sub LoadData(ByVal param As Object)
    End Class

    In the Abstract Method, we should load Data for the current view, and for that view, i should have a default multiple selection, here for example i have :
    SelectedItems = new ObservableCollection(Of Item)(AvailableItems)
    SelectedItems.RemoveAt(0)

    that way, it should select all values but the first one.

    Problem : There's no selection at all

    Tips / Fix ? :

    To do that i had to change a little bit that method :

        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


    to :


        Public Sub StartSynchronizing()        ListenForChangeEvents(_masterList)        ListenForChangeEvents(_targetList)        If Not TargetAndMasterCollectionsAreEqual() Then                ' 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)
                    SetListValuesFromSource(_targetList, _masterList, AddressOf ConvertFromTargetToMaster)        else                ' Update the Target list from the Master list                SetListValuesFromSource(_masterList, _targetList, AddressOf ConvertFromMasterToTarget)        End If    End Sub

    because the xaml is loaded After the datacontext, it tries to copy the selection of the listbox (an empty selection), to the "tracking" collection ( SelectedItems here)


    Did i miss something ? did i used it the bad way ? Is there a better way ?

    ReplyDelete
  65. -_-' it seems that some of my breakline char didn't printou very well :(
    I can't edit it, if someone here have admin rights to edit it and want to do it ... go ahead

    ReplyDelete
  66. TeBeCo,
       I think your fix is the best way to go.

    Sam

    ReplyDelete
  67. Has anyone tried using this with a collection that is filtered using an ICollectionView?  I.e. So I bind the collection view instead.  Whenever I update the collection view's filter and refresh, I expect there to be some sort of effect on SelectedItems for rows that are now filtered.  I assume, either (1) these filtered items are still part of my SelectedItems collections even though they are not visible on my UI; or (2) these filtered orders are magically removed from the SelectedItems collection.

    Unfortunately, this is not the case, and I am seeing both occurrences happening, i.e. sometimes the filter update results in the collections removing the filtered rows; sometimes it doesn't.  

    Anyone come across this?  I'm still trying to investigate further.

    ReplyDelete
  68. Thanks for providing a way for binding to SelectedItems, but I have reservations about your documentation strategy.

    For example, there is a method is called "Get Synchronized Selected Items()" whose 3-line documentation comment is: "Gets the synchronized selected items". It has a parameter called "dependencyObject" whose documentation is "The dependency object." See the pattern? The return value is documented as "The list that is acting as the sync list." This comment seems to provide more information than the others, but without a clear definition of "sync list", it actually doesn't.

    Ideally the documentation would tell us something that isn't immediately obvious, such as: why there are several classes (MultiSelectBehaviors, SynchronizationManager, TwoListSynchronizer) instead of just one? How do the classes fit together and how do they operate (e.g. what happens when the user clicks something? What happens when the collection changes on the viewmodel side?) What does the terminology mean (sync list, master list, target list...)? 

    ReplyDelete
  69. When I was a child, this was a matter of var selected = mylistbox.SelectedItems. Job done. pff kids these days..

    ReplyDelete
  70. Sorry, but this is not helpfull since it doesn't work properly when the seletedItems already have items in it. The suggested fix (swap the parameters) appears to work, but it does not! The items are indeed seleted in the ListBox, but when you press SelectedAll it shows 9 items selected, but only 8 are avaiable; and then if you continue to select/deselect more items nothing works anymore. Anyone fixed that?

    ReplyDelete
  71. Hi Sam,

    I am not sure why, but I can't get this to work. I have bound the SelectedItems like this in the User Control:



    In my VM I have:

    public ObservableCollection AvailableItems        {            get            {                return _availableItems;            }            set            {                
    _availableItems = value;                OnPropertyChanged("
    AvailableItems");            }        }        public ObservableCollection SelectedItems        {            get            {                return _selectedItems;            }            set            {                
    _selectedItems = value;                OnPropertyChanged("
    SelectedItems");            }        }

    My ViewModelBase implements the INotifyPropertyChanged from which I inherit my VM. The dependencyProperties SynchronizedSelectedItems and SynchronizationManagerProperty get initialized but the method OnSynchronizedSelectedItemsChanged is never called. 

    I bind my VM to VIew like this:

                        Report objReport = new Report();                    ReportViewModel objReportgVM = new ReportViewModel();                                        objReportgVM.ViewMenuItems = LoadHeaderView(ViewName);                    objReportgVM.WindowTitle = ViewName;                    objReportgVM.Load();                                        LoadChild(objReport, objReportgVM);

    And LoadChild method:
           private static void LoadChild(Object viewobj, Object viewmodelobj)        {            MWindow.pnlWorkArea.Children.Clear();            if (viewobj != null)            {                ((UserControl)viewobj).DataContext = viewmodelobj;                MWindow.pnlWorkArea.Children.Add((UIElement)viewobj);            }        }

    I am not sure what am I doing wrong. Please help, I am stuck so bad.

    ReplyDelete
  72. What exactly is it that doesn't work here? Is it that your view model needs to know when the SelectedItems collection has changed? In that case, you need to handle the ObservableCollection.CollectionChanged event.

    ReplyDelete
  73. Well, the ReceiveWeakEvent method was not being called. My guess was that the dependency property was not getting attached to the ListBox Selected Items. But some miracle happened and I have a working solution. Wow, I was wondering if I would be able to even make it work, but thanks to you, works like a charm. Thanks a tonne for this wonderful solution.

    ReplyDelete
  74. Hey Sam, Very good article. Helped me with a custom control I am developing, similar to "Column Options" dialog in the TFS workitem query results window. Thanks.

    ReplyDelete
  75. It would be nice if you could "bore" us a little with a code walk through for beginners like I, who have trouble understanding the TowListSynchronizer and MultiSelectedBehaviors class

    ReplyDelete
  76. Thank you for your solution! It became very useful for me!

    ReplyDelete
  77. The best solution of 'SelectedItems', I think. Works great!

    ReplyDelete
  78. This is awesome. Worked like a charm and will really help me stick to a more strict MVVM implementation.

    ReplyDelete
  79. The link to the source code doesn't seem to work for me. Could you check it or arrange another way for me to get the source. Thanks

    ReplyDelete
  80. @samuel_d_jack ,
    Great post. I just downloaded your code and tried it in VS 2013. Everything works except the "Select All" button. When you click "Select All", it deselects everything in the ListBox, then shows in the Count that all 8 items are selected. When I debugged it, I was able to verify that the ListBox's SelectedItems property did indeed have 8 items in it, but it didn't visually show them as selected. I was very confused.
    Then I upgraded the sample from WPF 3.5 to WPF 4.5.1. I ran it again and noticed that after clicking "Select All", there are now lines between each item. I realized that clicking the button set focus off of the ListBox, and thus I didn't know it was selected because when not focused, the default selected style looks an awful lot like the selected style.
    So, it works perfectly. Thanks again for such a great post!
    -Joe

    ReplyDelete
  81. I am trying to convert it to Silverlight but can find a substitute for CollectionChangedEventManager. Anyone have ideas?

    ReplyDelete
  82. Sam, did you get a chance to convert it to Silverlight. I tried, but there are lot of libraries missing in Silverlight.

    ReplyDelete
  83. Thank you for the article

    ReplyDelete
  84. Marcos Cobeña Morián18 July 2014 at 10:51

    Hello Sam,

    Thanks so much for such a nice piece of code, we ended up using it in one of our projects.

    Let me share with you an issue? we're having with your solution (I'll make use of your code at GitHub to explain it), are you aware of any way to avoid firing the _selectedNames.CollectionChanged event on every modification made to the list? In other words, we'd ideally like to have it just fired at the end, just once.

    Again, thanks for your work!

    Cheers,

    Marcos

    ReplyDelete
  85. Hi Sam. This works great, thank you so much! Would you happen to have a similar synchronizer for selected items in a TreeView with CheckBoxes?

    ReplyDelete
  86. Sorry - I've not done anything similar for TreeViews

    ReplyDelete