Given a ListView bound to items that have been grouped using a PropertyGroupDescription, is it possible to programmatically scroll so that a group is placed at the top of the list? I am aware that I can scroll to the first item in the group, since that item belongs to the collection the ListView is bound to. However, I have not been able to find any resources describing how to scroll to a group header (styled with a GroupStyle).
To give an example of the desired functionality, let's look at the settings page in Visual Studio Code. This page consists of a panel that allows the user to scroll through all of the application's settings (organized under their respective groups) as well as a tree structure on the left for faster navigation to a specific group in the main panel. In the screenshot attached, I clicked the Formatting option in the tree on the left, and the main panel automatically scrolled so that the corresponding group header was place at the top of the main panel.
How can this be recreated in WPF (if at all possible)? Could the "infinite" scrolling of the main settings panel in Visual Studio Code be mimicked with another WPF control?

The tree on the left (the TOC) has root nodes (the sections e.g. 'TextEditor'). Each section holds categories of settings (e.g. 'Formatting'). The
ListViewon the right (settings view) has items that have a group header with category names that match those of the TOC (e.g. Formatting).1. Edit to address the use of
PropertyGroupDescriptionAssumptions:
CollectionViewSourcedefined inside aResourceDictionaryand named CollectionViewSource.SettingsCategoryName(e.g. Formatting).SettingsCategoryNameof theSelectedItemof theTreeViewis bound to a propertySelectedSettingsCategoryNameView.xaml:
View.xaml.cs:
Find the selected category and scroll it to the top of the viewport.
You can move this method into a
ListViewderived type. Then add aCommandBindingsto the new customListViewthat handles a Routed Command e.g.ScrollToSectionRoutedCommand. Template theTreeViewItemsto be aButtonand let them emit the command to pass the section name asCommandParameterto the customListView.Remarks
Since the use of
PropertyGroupDescriptionresults in an items source of mixed data types (GroupItemDatafor the group headers and in addition the actual data items) the UI virtualization of the hostingItemsControlis disabled and not possible (see Microsoft Docs: Optimizing performance: Controls). In this scenario the attached propertyScrollViewer.CanContentScrollis automatically set toFalse(forced). For big list this can be a huge drawback and a reason to follow an alternative approach.2. Alternative solution (with UI virtualization support)
There are several possible variations when it comes to the design of the actual settings structure. It can be a tree where each category header node has its own child nodes which represent settings of a category or a flat list structure where category headers and settings are all siblings. For the sake of the example's simplicity I choose the second option: a flat list data structure.
2.1 The setup
Basic idea:
the
TreeViewis templated using aHierarchicalDataTemplatewith two levels. The second level of theTreeView(leafs) andListViewshare the same instances of the header items (IHeaederData. See later). Therefore the selected header item of theTreeViewreferences the exact same item header in theListView- no search required.Implementation overview:
ItemsControlelements:TreeViewfor the navigation pane on the left with two levelsListViewfor the actual settings and their category headers.IDatawith shared attributes (e.g. a header)IHeaderDataISettingDataTreeViewimplement an additionalISectionDatawhich has children of typeIHeaderDataIEnumerable<IData>)TreeView(which holds the categories only), aSectionCollectionof typeISectionDataCategoryCollectionof typeIHeaderDataSettingCollectionof typeIDataISectionDatato the source collectionSectionCollectionof theTreeViewIHeaderDatato both source collectionsCategoryCollectionandSettingCollectionISettingData, one for each setting of the category, to theSettingCollectiononlyCategoryCollectionto the child collection of theISectionDataroot nodeSectionCollectionto theTreeViewSettingsCollectionto theLIstViewHierarchicalDataTemplatefor theTreeViewdata whereISectionDatatype is the rootDataTemplatefor theListViewIHeaderDataISettingDataThe logic:
IHeaderDataitem of theTreeViewis selected thenListViewitem container of this data item usingvar container = ItemsContainerGenerator.GetContainerFromItem(selectedTreeViewCategoryItem)container.BringIntoView()(to realize virtualized items that are out of view)Because
TreeViewandListViewshare the same category header data (IHeaderData) the selected items are easy to track and to find. You don't have to search the group of settings. You can directly jump to the group using the reference. This means the structure of the data is the key of the solution.