0

I have a TreeView that must be virtualized for performance reasons. The problem is that if I change the bound IsSelected property the TreeViewItem.Selected event is not called until it is visible (when scrolled down).

So I can not implement the 'BringIntoView when selected pattern' that is demonstrated here.

I know there was answered already a similiar question. But the answers give no real solution. And I have no credits to add comments so far.

How can I overcome this?

Here is my sample code:

XAML:

<Window x:Class="VirtualizedTreeView.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:VirtualizedTreeView"
        mc:Ignorable="d"
        x:Name="Window"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button Grid.Row="0"
                Content="Select Element 5"
                Click="Button_Click_5" />
        <Button Grid.Row="1"
                Content="Select Element 500"
                Click="Button_Click_500" />
        <TreeView Grid.Row="2"
                  VirtualizingStackPanel.IsVirtualizing="True"
                  VirtualizingStackPanel.VirtualizationMode="Recycling"
                  ScrollViewer.CanContentScroll="True"
                  
                  ItemsSource="{Binding ElementName=Window, Path=Items}" >
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsSelected"
                            Value="{Binding IsSelected}" />
                    <EventSetter Event="TreeViewItem.Selected"
                                 Handler="TreeViewItem_Selected" />
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>
    </Grid>
</Window>

Xaml.cs:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            for (var i = 0; i < 1000; i++)
            {
                Items.Add(new ItemsViewModel($"Item{i}"));
            }
        }

        public Collection<ItemsViewModel> Items { get; }
            = new Collection<ItemsViewModel>();

        private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
        {
            // Fired immediately for Item 5 but not for item 500
        }

        private void Button_Click_5(object sender, RoutedEventArgs e)
        {
            Items[5].IsSelected = true;
        }

        private void Button_Click_500(object sender, RoutedEventArgs e)
        {
            Items[500].IsSelected = true;
        }
    }

ItemViewModel.cs:

    public class ItemViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string header;

        public ItemViewModel(string header)
        {
            this.header = header;
        }

        public override string ToString()
            => header;

        private bool isSelected;
        public bool IsSelected
        {
            get => isSelected;
            set
            {
                if (isSelected != value)
                {
                    isSelected = value;
                    OnNotifyPropertyChanged();
                }
            }
        }

        private void OnNotifyPropertyChanged([CallerMemberName] String propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

I already tried to use VirtualizationMode.Standard and using ScrollViewer.CanContentScroll="True".

I could try to somehow set the BringIntoView manually when IsSelected is set to true in the ViewModel but this feels a bit clumsy.

Lord Tasci
  • 21
  • 4
  • Why are you putting so much into your UI that virtualization is so significant? – Andy Mar 09 '23 at 13:38
  • The code is too big to post as an answer here, but check out my blog post on TreeView controls in a WPF / MVVM context - http://peregrinesview.uk/wpf-behaviors-part-2-treeview/. In the second demo project, the underlying data structure has over 400,000 nested items, but the UI only creates the bare minimum of controls to display it, and allows random seeking to any node at any level of the structure. – Peregrine Mar 09 '23 at 21:01

0 Answers0