Reactive Extensions with #uwpdev Part 3

The way I understood Rx, it was designed to do away with how we (developers) handle events.

A ListView control provides a few ways of handling how developers deal with user interaction. You can have ItemClick and disable Item Selection. You could have Item Selection with ability to select more than one Item.
My personal favourite is to allow ItemClick and to bind a Command property in ListViewExtensions in UWP Toolkit.

There are times when you really need to handle the event in code behind – I know purists would have a day here but say you had a video previews running in a list view. You don’t want all of them running all the time – you want to ensure that only those rendered on screen are running the video.
How would one do that ?

The usual route would be to find the ScrollViewer in the ListView control and hook into the ViewChanged event.
Every time ViewChanged event is raised, OnScrollViewerViewChanged method will be called.

By every time I mean every time.. could be maybe times every second. You then start have to add hacks like lastChecked time and if Time Elapsed is greater than 100ms etc etc.

private void WireUpListViewScroll(ListView listView)
{
    if (listView == null)
    {
        return;
    }

    if (scrollViewer != null)
    {
        scrollViewer.ViewChanged -= OnScrollViewerViewChanged;
    }

    var scrollViewer = listView.FindDescendant<ScrollViewer>();

    if (scrollViewer == null)
    {
        return;
    }

    scrollViewer.ViewChanged += OnScrollViewerViewChanged;
    
    ProcessChannelUserControls(listView, true);
}

private void OnScrollViewerViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
    this.ProcessChannelUserControls(listView);
}

So lets look up what Reactive extensions has for us. For starters, Observable.FromEventPattern allows you to create an observable from event and to subscribe to it. No need to unsubscribe from event handers etc.. just dispose the subscription.

Notice I also use Throttle with a TimeSpan.. I don’t want too many hits – once every 0.35 seconds seemed sufficiently responsive for my use and what’s what I used. the scrollViewerSub I use is a SerialDisposable – every time I add a new subscription, it disposes the old one.

private void WireUpListViewScroll(ListView listView)
{
    if (listView == null)
    {
        return;
    }

    var scrollViewer = listView.FindDescendant<ScrollViewer>();

    if (scrollViewer == null)
    {
        return;
    }

    this.scrollViewerSub.Disposable = Observable.FromEventPattern(scrollViewer, "ViewChanged")
        .Throttle(TimeSpan.FromSeconds(0.35))
        .ObserveOn(CoreDispatcherScheduler.Current)
        .SubscribeOn(TaskPoolScheduler.Default)
        .Subscribe(x =>
        {
            this.ProcessChannelUserControls(listView);
        });

    ProcessChannelUserControls(listView, true);
}

Now that the ViewChanged logic is in place, let’s look at the rest of the code for toggling video previews.

private async void ProcessChannelUserControls(ListView listview, bool initial = false)
{
    try
    {
        var isp = listview.ItemsPanelRoot as ItemsStackPanel;

        if (isp == null)
        {
            return;
        }

        await ToggleAnimatedPreviews(listview, isp, initial);
    }
    catch { }
}

private async Task ToggleAnimatedPreviews(ListView listView, ItemsStackPanel isp, bool initial)
{
    var firstGroupPos = isp.FirstVisibleIndex;
    var lastGroupPos = isp.LastVisibleIndex;

    if (firstGroupPos == -1 && lastGroupPos == -1)
    {
        firstGroupPos = 0;
        lastGroupPos = 0;
    }

    if (initial)
    {
        DependencyObject obj = null;
        while (true)
        {
            obj = listView.ContainerFromIndex(0);

            if (obj != null)
            {
                break;
            }

            await Task.Delay(100);
        }
    }

    for (int i = 0; i < listView.Items.Count; i++)
    {
        var container = listView.ContainerFromIndex(i);

        var vp = container?.FindDescendant<AnimatedPreviewPlayerUC>();

        if (i >= firstGroupPos && i <= lastGroupPos)
        {
            vp?.Play();
        }
        else
        {
            vp?.Pause();
        }
    }
}

These are some of the places I have used Reactive Extensions whilst doing #uwpdev with MVVM. I am still learning.

Leave a comment