WPF – Custom IsSynchronizedWithCurrentItem implementation

Hi guys,

if you ever missed the IsSynchronizedWithCurrentItem property in one of your controls you might need this.
Well basically you can enable a feature using this property which is synchronizing the selected item of your Control (ListBox, ComboBox, etc.) with the current item of the collection to which you bind to.

In my case I needed this feature in the Telerik RadListBox. Therefore I created an attached property which simulates this feature.
Basically what I’m doing is the following:

  • When the SelectedItem in the ListBox was changed => update the CurrentItem in the Collection
  • When the CurrentItem in the Collection was changed => update the SelectedItem of the ListBox

The tricky part here is that the user might bind a new collection to the control. Such that you have to notice that the collection was changed and register the events for the new collection.
Therefore I used this ListBoxSources-Dictionary which contains the key value pairs of the listboxes and their corresponding collections.
If the collection is different from the one it has before we know that it was changed and have to unregister the events in the old collection and register then in the new collection.

Here is the code I was talking about:

 /// <summary>
  ///   This class contains a few useful extenders for the ListBox.
  /// </summary>
  public class RadListBoxExtensions : DependencyObject
  {
    private static readonly Dictionary<RadListBox, object> ListBoxSources = new Dictionary<RadListBox, object>();

    public static readonly DependencyProperty IsSynchronizedWithCurrentItemProperty = DependencyProperty.RegisterAttached("IsSynchronizedWithCurrentItem", typeof (bool), typeof (RadListBoxExtensions), new UIPropertyMetadata(default(bool), OnIsSynchronizedWithCurrentItemChanged));

    /// <summary>
    ///   Returns the value of the IsSynchronizedWithCurrentItemProperty
    /// </summary>
    /// <param name="obj">The dependency-object whichs value should be returned</param>
    /// <returns>The value of the given property</returns>
    public static bool GetIsSynchronizedWithCurrentItem(DependencyObject obj)
    {
      return (bool) obj.GetValue(IsSynchronizedWithCurrentItemProperty);
    }

    /// <summary>
    ///   Sets the value of the IsSynchronizedWithCurrentItemProperty
    /// </summary>
    /// <param name="obj">The dependency-object whichs value should be set</param>
    /// <param name="value">The value which should be assigned to the AutoScrollToCurrentItemProperty</param>
    public static void SetIsSynchronizedWithCurrentItem(DependencyObject obj, bool value)
    {
      obj.SetValue(IsSynchronizedWithCurrentItemProperty, value);
    }

    /// <summary>
    ///   This method will be called when the IsSynchronizedWithCurrentItem property was changed
    /// </summary>
    /// <param name="s">The sender (the RadListBox)</param>
    /// <param name="e">Some additional information</param>
    public static void OnIsSynchronizedWithCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
    {
      var listBox = s as RadListBox;
      if (listBox != null)
      {
        var newValue = (bool) e.NewValue;
        var selectionChangedWorker = new SelectionChangedEventHandler((s1, e2) => OnSelectionChanged(listBox, listBox.SelectedItem));
        if (newValue)
        {
          listBox.Loaded += OnLoaded;
          listBox.SelectionChanged += selectionChangedWorker;
        }
        else
        {
          listBox.Loaded -= OnLoaded;
          listBox.SelectionChanged -= selectionChangedWorker;
        }
      }
    }

    /// <summary>
    ///   Called when when the listbox was loaded.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">
    ///   The <see cref="DependencyPropertyChangedEventArgs" /> instance containing the event data.
    /// </param>
    private static void OnLoaded(object sender, RoutedEventArgs e)
    {
      var listBox = sender as RadListBox;
      if (listBox == null)
        return;
      var collection = listBox.ItemsSource;
      ActivateCollectionIfNecessary(listBox, collection);
    }

    /// <summary>
    ///   Called when the selection was changed.
    /// </summary>
    /// <param name="listBox">The list box.</param>
    /// <param name="selectedItem">The selected item.</param>
    private static void OnSelectionChanged(RadListBox listBox, object selectedItem)
    {
      var collection = listBox.ItemsSource;
      ActivateCollectionIfNecessary(listBox, collection);
      var defaultView = CollectionViewSource.GetDefaultView(collection);
      if (defaultView != null)
        defaultView.MoveCurrentTo(selectedItem);
    }

    /// <summary>
    ///   Activates the collection if necessary.
    /// </summary>
    /// <param name="listBox">The listbox.</param>
    /// <param name="collection">The collection.</param>
    private static void ActivateCollectionIfNecessary(RadListBox listBox, object collection)
    {
      if (!ListBoxSources.ContainsKey(listBox))
        ListBoxSources.Add(listBox, collection);
      else if (ListBoxSources[listBox] == collection)
        return;
      var oldCollection = ListBoxSources[listBox];
      var newView = CollectionViewSource.GetDefaultView(collection);
      var currentChangedWorker = new EventHandler((s1, e2) => listBox.SelectedItem = newView.CurrentItem);

      if (oldCollection != null)
      {
        var oldView = CollectionViewSource.GetDefaultView(oldCollection);
        if (oldView != null)
          oldView.CurrentChanged -= currentChangedWorker;
      }
      ListBoxSources[listBox] = collection;

      if (newView != null)
      {
        newView.CurrentChanged += currentChangedWorker;
        listBox.SelectedItem = newView.CurrentItem;
      }
    }
}

Have fun!

Advertisements
  1. I think this has promise, but, could you show your entire Xaml declaration for the RadListBox. I am setting one of the properties wrong and this fires but basically does nothing. Here is mine, which you can just beat up where obviously wrong.
    ThemeInfo is the ListItem object, consisting of Name InternalName and Description. The ItemsSource is a collection of ThemeInfo.

    Thanks.



  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: