I had a requirement in a Metro app I’m working on to detect scrolling in a ListView (GridView is practically the same), or more precisely, detect whether the selected item goes off the visible ListView area, and if so, switch some items in the ListView so that the selected item be visible again; this is not an entirely accurate description, but it’s close enough for our purposes. An easy one, right?
Searching the ListView class (and its bases) yields no useful results on scrolling. In WPF, the ScrollViewer element has an attached event, ScrollChanged. This can be used (in WPF) to detect scrolling on any control that has a ScrollViewer as part of its control template (such as a ListBox).
Surprisingly (at least it was for me), the WinRT ScrollViewer has no such event. This makes it hard to detect scroll changes.
Unless I missed something obvious (which I don’t think I did), I had to try something unorthodox. I expected at least to get a chance to ask for the scroll position. ScrollViewer has a VerticalOffset and HorizontalOffset properties, which I theoretically could have used with a timer, to detect changes in the vertical scroll offset; but alas, in WinRT it’s not an attached property as it is in WPF. Bummer!
So, how can we solve this without creating a new template and hook into that?
I created a DispatcherTimer to expire every second, and when it did, I calculated the location of the selected item like so:
- auto view = _cvs->View;
- auto xform = _listView->TransformToVisual((UIElement^)_listView->ItemContainerGenerator->ContainerFromIndex(view->CurrentPosition));
- auto point = xform->TransformPoint(Point(0, 0));
Yes, this is C++/CX code… (see my previous post on that), but in this case (at least), the translation to C# is straightforward.
The view variable is an ICollectionView (returned from a CollectionViewSource object (_cvs)). TransformToVisual returns a GeneralTransform object that we can use to translate Points and Rects relative to the specified element (in this case the selected item). Transforming the origin point (0,0) of that element returns a new Point; if this point falls outside the boundaries of the ListView, then some scrolling occurred that obscured the selected item.
I find it annoying that Windows 8 with WinRT is generally less powerful than WPF. I expect a new platform to be more powerful and flexible, not less so. Granted, there are some new niceties that I wish WPF had (such as Transitions), but overall, I feel a lot is missing relative to WPF (not so much relative to Silverlight, but even Silverlight 5 is more powerful – the DataTemplate.DataType property is an obvious example).
Hopefully, things will improve at RTM time (and beyond).