というネタを見つけたのでやってみます。久しぶりの WPF ネタ!因みにせっかくなので .NET Core 3.0 Preview 7 で VS2019 Preview 使ってやってみます。
表示用データはこれ!
using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace TreeViewScrollSample { publicclass Person { publicstring Name { get; set; } public IEnumerable<Person> Children { get; set; } } }
画面が側はこんな感じでいきましょう。
<Windowx:Class="TreeViewScrollSample.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:TreeViewScrollSample"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="MainWindow"Width="800"Height="450"mc:Ignorable="d"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition /></Grid.RowDefinitions><Button Click="ScrollButton_Click"Content="Scroll" /><TreeView x:Name="treeView"Grid.Row="1"><TreeView.ItemTemplate><HierarchicalDataTemplate ItemsSource="{Binding Children}"><TextBlock Text="{Binding Name}" /></HierarchicalDataTemplate></TreeView.ItemTemplate></TreeView></Grid></Window>
これで、こんな感じでコードを書けばスクロールします。
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; namespace TreeViewScrollSample { /// <summary>/// Interaction logic for MainWindow.xaml/// </summary>publicpartialclass MainWindow : Window { private IEnumerable<Person> People { get; } = Enumerable.Range(1, 100) .Select(x => new Person { Name = $"Tanaka {x}", Children = Enumerable.Range(1, 10) .Select(y => new Person { Name = $"Kimura {x}-{y}", }) .ToArray(), }) .ToArray(); public MainWindow() { InitializeComponent(); treeView.ItemsSource = People; } private async void ScrollButton_Click(object sender, RoutedEventArgs e) { // ContainersGenerated になるまで待つ Task waitUntilContainersGenerated(TreeViewItem container) { var tcs = new TaskCompletionSource<object>(container); void statusChanged(object _, EventArgs __) { if (container.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { tcs.SetResult(null); container.ItemContainerGenerator.StatusChanged -= statusChanged; return; } } container.ItemContainerGenerator.StatusChanged += statusChanged; return tcs.Task; } // 一番最後の最後の要素にスクロールする予定 var parent = People.Last(); var target = parent.Children.Last(); // ツリービュー直下の最後の要素のTreeViewItemを取得 var container = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(parent); // 開く container.IsExpanded = true; // 子の ItemContainerGenerator のステータスが Generated になるまで待つif (container.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) { await waitUntilContainersGenerated(container); } // スクロール先を取得してスクロール var targetContainer = (TreeViewItem)container.ItemContainerGenerator.ContainerFromItem(target); targetContainer.BringIntoView(); } } }
実行すると…
動いた!!
ソースコードは GitHub に上げておきました。