Search code examples
xamluwpc#-3.0master-detailwindows-community-toolkit

MasterDetailView UWP - Access the ListView (or make it scroll)


I recently discovered the UWP Community Toolkit and I loved it. I'm using now the MasterDetailView and it's almost perfect for what I need: but it's missing one important thing: I cannot access the internal "ListView".

In my application I have two buttons: forward and back, and when pressing them I simply go forward and back in the list. I found a trick to access the prev/next element doing like this:

public void GoForward()
    {
        if (MasterDetailView.Items.Count > 0)
        {
            bool isNextGood = false;
            MyItem selectedItem = MasterDetailView.Items[MasterDetailView.Items.Count - 1] as MyItem;

            foreach (var v in MasterDetailView.Items)
            {
                if (isNextGood)
                {
                    selectedItem = v as MyItem;
                    break;
                }

                if (v == MasterDetailView.SelectedItem)
                    isNextGood = true;
            }

            MasterDetailView.SelectedItem = selectedItem;
        }
    }

This just because I cannot access the "SelectedIndex" and I have only SelectedItem avaiable. Now, obviously not all the item can be visible at the same time, so MasterDetailView provide a lateral ListView with a scrollbar. When pressing my next/prev buttons SelectedItem changes, but doesn't scroll at the selected element: selection goes forward/back but the list is locked. This produce a very negative feedback because I lost my selection somewhere in the list and I must search it.

How I though to solve it? I try this approaches:


1) Find the style for MasterDetailView. Inside I found a ListViewStyle, so I tried to put inside a simple "SelectionChanged" event and handle it at App.xaml.cs.

<ListView x:Name="MasterList"
                                      Grid.Row="1"
                                      IsTabStop="False"
                                      ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
                                      ItemTemplate="{TemplateBinding ItemTemplate}"
                                      ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                                      ItemsSource="{TemplateBinding ItemsSource}"
                                      SelectedItem="{Binding SelectedItem, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                      SelectionChanged="MasterList_SelectionChanged"/>

CS:

private void MasterList_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ListView list = sender as ListView;
        list.ScrollIntoView(list.SelectedItem);
    }

But, as said, "Events cannot be set in the Application class XAML file".


2) Think about taking the parent of SelectedItem: I tried to convert the SelectedItem in ListViewItem, then access the parent, but it fails at first conversion, as the SelectedItem seems no to be a ListViewItem, but it's of the "MyItem" type. Like this, I cannot access the parent.

private void MasterDetailView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var item = MasterDetailView.SelectedItem as ListViewItem;
        var parent = item.Parent;
        var list = parent as ListView;
    }

And so I'm here... I don't want to throw away all my work with the MasterDetailView to pass to another control. Is there any simple method to access the list, or simply, scroll the list when I'm changing selection? Just wanna to do one thing, like this:

private void List_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ListView list = sender as ListView;
            list.ScrollIntoView(list.SelectedItem);
        }

Simply scrolling into selection when selection changed occurred, but I have no simple ListView but a MasterDetailView control. Even if it's done entirely in XAML: the most important thing for me is make scroll this list!

Thanks.


   

Solution

This method is fantastic. Just copy-paste.

public static T FindChildOfType<T>(DependencyObject root) where T : class
    {
        var queue = new Queue<DependencyObject>();
        queue.Enqueue(root);
        while (queue.Count > 0)
        {
            DependencyObject current = queue.Dequeue();
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(current); i++)
            {
                var child = VisualTreeHelper.GetChild(current, i);
                var typedChild = child as T;
                if (typedChild != null)
                {
                    return typedChild;
                }
                queue.Enqueue(child);
            }
        }
        return null;
    }

Then simply use it to retrive the list and make it scroll. Yes babe!

private void MasterDetailView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var v = FindChildOfType<ListView>(MasterDetailView);
        v.ScrollIntoView(MasterDetailView.SelectedItem);
    }

Please note that "MasterDerailView" Is the x:Name of my element, not the class.


Solution

  • You can use a FindChildOfType implementation to get the ListView through VisualTreeHelper, as indicated in this answer: Windows UWP - How to programmatically scroll ListView in ContentTemplate