Lost of selection via GridViewExtensions.BindableSelection after NavigateTo

Feb 7, 2013 at 3:59 PM
Edited Feb 7, 2013 at 4:01 PM
Hi,
as I mentioned in my first post I'm using GridViewExtensions.BindableSelection to track my selections of my GridViews in my ViewModel.
In the following scenarion I'm missing my selected GridViewItems:
  • Select some items
  • Navigate to another Page.
  • Navigate back.
  • Selections are missing although the items are still in the OC.
    Any ideas how to solve this problem?
    Do I have to rebind to the OCs during OnNavigatedTo? How?
Thanks in advance.
Darius
Coordinator
Feb 7, 2013 at 4:14 PM
I'll check it once I have a moment.
Coordinator
Feb 8, 2013 at 7:05 PM
Feb 12, 2013 at 4:14 PM
I needed a solution for the GridView, but this post helped a lot.
Here are my changes.
/// <remarks>
    /// Note: ListViewExtensions can be used for GridViews as well, since the extensions actually work on ListViewBase
    /// which both ListView and GridView derive from.
    /// </remarks>
    public static class GridViewExtensions
    {
        #region BindableSelection

        /// <summary>
        /// BindableSelection Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty BindableSelectionProperty =
            DependencyProperty.RegisterAttached(
                "BindableSelection",
                typeof(object),
                typeof(GridViewExtensions),
                new PropertyMetadata(null, OnBindableSelectionChanged));

        /// <summary>
        /// Gets the BindableSelection property. This dependency property
        /// indicates the list of selected items that is synchronized
        /// with the items selected in the GridView.
        /// </summary>
        public static ObservableCollection<object> GetBindableSelection(DependencyObject d)
        {
            return (ObservableCollection<object>)d.GetValue(BindableSelectionProperty);
        }

        /// <summary>
        /// Sets the BindableSelection property. This dependency property
        /// indicates the list of selected items that is synchronized
        /// with the items selected in the GridView.
        /// </summary>
        public static void SetBindableSelection(
            DependencyObject d,
            ObservableCollection<object> value)
        {
            d.SetValue(BindableSelectionProperty, value);
        }

        /// <summary>
        /// Handles changes to the BindableSelection property.
        /// </summary>
        /// <param name="d">
        /// The <see cref="DependencyObject"/> on which
        /// the property has changed value.
        /// </param>
        /// <param name="e">
        /// Event data that is issued by any event that
        /// tracks changes to the effective value of this property.
        /// </param>
        private static void OnBindableSelectionChanged(
            DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            dynamic oldBindableSelection = e.OldValue;
            dynamic newBindableSelection = d.GetValue(BindableSelectionProperty);

            if ( oldBindableSelection != null )
            {
                var handler = GetBindableSelectionHandler(d);
                SetBindableSelectionHandler(d, null);
                handler.Detach();
            }

            if ( newBindableSelection != null )
            {
                var handler = new GridViewBindableSelectionHandler(
                    (GridView)d, newBindableSelection);
                SetBindableSelectionHandler(d, handler);
            }
        }

        #endregion BindableSelection

        #region BindableSelectionHandler

        /// <summary>
        /// BindableSelectionHandler Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty BindableSelectionHandlerProperty =
            DependencyProperty.RegisterAttached(
                "BindableSelectionHandler",
                typeof(GridViewBindableSelectionHandler),
                typeof(GridViewExtensions),
                new PropertyMetadata(null));

        /// <summary>
        /// Gets the BindableSelectionHandler property. This dependency property
        /// indicates BindableSelectionHandler for a GridView - used
        /// to manage synchronization of BindableSelection and SelectedItems.
        /// </summary>
        public static GridViewBindableSelectionHandler GetBindableSelectionHandler(
            DependencyObject d)
        {
            return
                (GridViewBindableSelectionHandler)
                d.GetValue(BindableSelectionHandlerProperty);
        }

        /// <summary>
        /// Sets the BindableSelectionHandler property. This dependency property
        /// indicates BindableSelectionHandler for a GridView - used to manage synchronization of BindableSelection and SelectedItems.
        /// </summary>
        public static void SetBindableSelectionHandler(
            DependencyObject d,
            GridViewBindableSelectionHandler value)
        {
            d.SetValue(BindableSelectionHandlerProperty, value);
        }

        #endregion BindableSelectionHandler

        #region ItemToBringIntoView

        /// <summary>
        /// ItemToBringIntoView Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty ItemToBringIntoViewProperty =
            DependencyProperty.RegisterAttached(
                "ItemToBringIntoView",
                typeof(object),
                typeof(GridViewExtensions),
                new PropertyMetadata(null, OnItemToBringIntoViewChanged));

        /// <summary>
        /// Gets the ItemToBringIntoView property. This dependency property
        /// indicates the item that should be brought into view.
        /// </summary>
        public static object GetItemToBringIntoView(DependencyObject d)
        {
            return (object)d.GetValue(ItemToBringIntoViewProperty);
        }

        /// <summary>
        /// Sets the ItemToBringIntoView property. This dependency property
        /// indicates the item that should be brought into view when first set.
        /// </summary>
        public static void SetItemToBringIntoView(DependencyObject d, object value)
        {
            d.SetValue(ItemToBringIntoViewProperty, value);
        }

        /// <summary>
        /// Handles changes to the ItemToBringIntoView property.
        /// </summary>
        /// <param name="d">
        /// The <see cref="DependencyObject"/> on which
        /// the property has changed value.
        /// </param>
        /// <param name="e">
        /// Event data that is issued by any event that
        /// tracks changes to the effective value of this property.
        /// </param>
        private static void OnItemToBringIntoViewChanged(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            object newItemToBringIntoView =
                (object)d.GetValue(ItemToBringIntoViewProperty);

            if ( newItemToBringIntoView != null )
            {
                var GridView = (GridView)d;
                GridView.ScrollIntoView(newItemToBringIntoView);
            }
        }

        #endregion ItemToBringIntoView

        /// <summary>
        /// Scrolls a vertical GridView to the bottom.
        /// </summary>
        /// <param name="GridView"></param>
        public static void ScrollToBottom(this GridView GridView)
        {
            var scrollViewer = GridView.GetFirstDescendantOfType<ScrollViewer>();
            scrollViewer.ScrollToVerticalOffset(scrollViewer.ScrollableHeight);
        }
    }
Feb 12, 2013 at 4:14 PM
And
public class GridViewBindableSelectionHandler
    {
        private GridView _GridView;
        private dynamic _boundSelection;
        private readonly NotifyCollectionChangedEventHandler _handler;

        public GridViewBindableSelectionHandler(
            GridView GridView, dynamic boundSelection)
        {
            _handler = OnBoundSelectionChanged;
            Attach(GridView, boundSelection);
        }

        private void Attach(GridView GridView, dynamic boundSelection)
        {
            _GridView = GridView;
            //_GridView.Unloaded += OnGridViewUnloaded;
            _GridView.SelectionChanged += OnGridViewSelectionChanged;
            _boundSelection = boundSelection;
            _GridView.SelectedItems.Clear();

            foreach ( object item in _boundSelection )
            {
                if ( !_GridView.SelectedItems.Contains(item) )
                {
                    _GridView.SelectedItems.Add(item);
                }
            }

            var eventInfo =
                _boundSelection.GetType().GetDeclaredEvent("CollectionChanged");
            eventInfo.AddEventHandler(_boundSelection, _handler);
            //_boundSelection.CollectionChanged += OnBoundSelectionChanged;
        }

        private void OnGridViewSelectionChanged(
            object sender, SelectionChangedEventArgs e)
        {
            foreach ( dynamic item in e.RemovedItems )
            {
                if ( _boundSelection.Contains(item) )
                {
                    _boundSelection.Remove(item);
                }
            }
            foreach ( dynamic item in e.AddedItems )
            {
                if ( !_boundSelection.Contains(item) )
                {
                    _boundSelection.Add(item);
                }
            }
        }

        private void OnBoundSelectionChanged(
            object sender, NotifyCollectionChangedEventArgs e)
        {
            if ( e.Action ==
                NotifyCollectionChangedAction.Reset )
            {
                _GridView.SelectedItems.Clear();

                foreach ( var item in _boundSelection )
                {
                    if ( !_GridView.SelectedItems.Contains(item) )
                    {
                        _GridView.SelectedItems.Add(item);
                    }
                }

                return;
            }

            if ( e.OldItems != null )
            {
                foreach ( var item in e.OldItems )
                {
                    _GridView.SelectedItems.Remove(item);
                }
            }

            if ( e.NewItems != null )
            {
                foreach ( var item in e.NewItems )
                {
                    _GridView.SelectedItems.Add(item);
                }
            }
        }

        private void OnGridViewUnloaded(object sender, RoutedEventArgs e)
        {
            Detach();
        }

        internal void Detach()
        {
            //_GridView.Unloaded -= OnGridViewUnloaded;
            _GridView.SelectionChanged -= OnGridViewSelectionChanged;
            _GridView = null;
            var eventInfo =
                _boundSelection.GetType().GetDeclaredEvent("CollectionChanged");
            eventInfo.RemoveEventHandler(_boundSelection, _handler);
            _boundSelection = null;
        }
    }
Feb 12, 2013 at 4:18 PM
Are you going to integrate these changes to the next release?

Thank you for your help.

Btw. This solution also solved my first problem http://winrtxamltoolkit.codeplex.com/discussions/432172
Coordinator
Feb 25, 2013 at 9:00 AM
Pushed the updated source code. As for CodePlex/NuGet releases - I will probably wait until I have made some more planned changes to WriteableBitmap.Render() extension, fix/remove (replace with WinRTBehaviors) implementation of Behaviors and perhaps include WinRTTriggers.