This is a migrated thread and some comments may be shown as answers.

Virtualization and Bring[Item/Index]IntoView problems

8 Answers 459 Views
TreeView
This is a migrated thread and some comments may be shown as answers.
Ammaar
Top achievements
Rank 1
Ammaar asked on 14 Dec 2010, 12:44 PM
Hi, I have the following RadTreeView:

<tc:RadTreeView x:Name="tree"
                ItemsSource="{Binding CategoryCollectionView, UpdateSourceTrigger=PropertyChanged}"
                SelectionChanged="RadTreeView_SelectionChanged"
                LoadOnDemand="tree_LoadOnDemand"
                IsVirtualizing="True"
                tc:TreeViewPanel.VirtualizationMode="Standard"
                ta:AnimationManager.IsAnimationEnabled="False"
                IsDragTooltipEnabled="False"
                IsDragDropEnabled="True"
                SelectionMode="Multiple">
    <tc:RadTreeView.Resources>
        <Style TargetType="{x:Type ScrollBar}" />
 
        <HierarchicalDataTemplate DataType="{x:Type local:CategoryTreeItemViewModel}"
                                  ItemsSource="{Binding ChildrenCollectionView}">
             <!--snip-->
        </HierarchicalDataTemplate>
 
        <DataTemplate DataType="{x:Type local:EventNodeViewModel}">
            <!-- snip -->
        </DataTemplate>
 
    </tc:RadTreeView.Resources>
    <tc:RadTreeView.ItemContainerStyle>
        <Style TargetType="{x:Type tc:RadTreeViewItem}">
            <Setter Property="IsExpanded"
                    Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected"
                    Value="{Binding IsSelected, Mode=TwoWay}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding DataContext.IsMainCategoryTree, ElementName=tree}"
                             Value="True">
                    <Setter Property="IsLoadOnDemandEnabled"
                            Value="{Binding LoadOnDemand}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </tc:RadTreeView.ItemContainerStyle>
</tc:RadTreeView>

I am doing a refresh of the tree using a button which recreates all the item ViewModels in the tree and then I am attempting to restore the state of the tree i.e. expand previously expanded nodes and set previously selected item. This is all done from within the tree's ViewModel.

This all works fine as long as the user has not scrolled down. Unfortunately, when the user has scrolled down and a refresh happens, the user will then only see the root nodes (not expanded) unless they scroll back up to the top of the tree.

In order to work around this I subscribed to the tree's ItemContainerGenerator.StatusChanged event after refreshing the data to check when the items are finished generating and then I used the BringItemIntoView or BringIndexIntoView methods to bring to the selected item into view.

However, this doesn't currently work due to the following exception being thrown when either of these methods are called.:
Cannot call StartAt when content generation is in progress.

Any help would be appreciated with this problem,

Thanks,

A.

8 Answers, 1 is accepted

Sort by
0
Hristo
Telerik team
answered on 14 Dec 2010, 04:40 PM
Hello Ammaar,

Actually you should call the bring into view methods after the containers have been generated. Not when the container generator status has changed (meaning that you should wait for ContainersGenerated status).

Also you could use get item by path method (must called it after the tree view is loaded) and get the item container you want. The you can call the BringIntoView method of the RadTreeViewItem.

Hope this helps. Let me know if you need more help.

All the best,
Hristo
the Telerik team
Browse the videos here>> to help you get started with RadControls for WPF
0
Ammaar
Top achievements
Rank 1
answered on 14 Dec 2010, 06:38 PM
Well, originally I was doing this:
I fired an event from the ViewModel before refreshing data. This then invoked an event handler that switched off tree virtualization and subscribed to the ItemContainerGenerator.StatusChanged event:
ViewModel.BeforeTreeRefresh += ViewModel_BeforeTreeRefresh;

private void ViewModel_BeforeTreeRefresh( object sender, EventArgs e )
{
     tree.IsVirtualizing = false;
     tree.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}

Then after the data was finished refreshing I fired another event from my ViewModel (TreeRefreshed) and the handler for this event was supposed to bring the selected item into view and turn virtualization back on:
ViewModel.TreeRefreshed += ItemContainerGenerator_StatusChanged;

void ItemContainerGenerator_StatusChanged( object sender, EventArgs e )
{
    var itemContainerGenerator = sender as ItemContainerGenerator;
 
    if ( itemContainerGenerator != null && itemContainerGenerator.Status == GeneratorStatus.ContainersGenerated )
    {
        //find item to bring into view and call Bring[Item/Index]IntoView on it's container
 
           tree.IsVirtualizing = true;
        itemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
    }
}

However, this approach as I said in my original post was throwing an exception so I changed my approach.

Instead of bringing the selected item into view, I inherited the RadTreeView and created a public method which calls ChangeVisualState(). I then called this method where I was originally trying to bring the selected item into view.

This way the user will be in the same place in the tree as they were before they requested a refresh which is a more desirable behaviour than bringing the selected item into view as the user could select an item and then scroll away from it before they invoke a refresh.
0
Ammaar
Top achievements
Rank 1
answered on 15 Dec 2010, 06:07 PM
I seem to have another issue when refreshing the RadTreeView. Before refreshing items can be selected fine, but after the refresh, when the user selects items only the focus changes to that item while the selection does not change. I have an event handler for the SelectedItemChanged event, which gets fired before refresh, but not after refresh.

Any idea what is causing this? (screenshots attached)
0
Hristo
Telerik team
answered on 20 Dec 2010, 10:57 AM
Hi Ammaar,

Can you provide us a sample project demonstrating the issue in order to investigate it in detail. I tried to reproduce it but couldn't do it. The issue could also be associated to the ChangeVisualState call that you are making, but this is only a hypothesis.

All the best,
Hristo
the Telerik team
Browse the videos here>> to help you get started with RadControls for WPF
0
Ristogod
Top achievements
Rank 2
answered on 21 Jun 2011, 07:43 PM
Can you please give an example of what you are talking about. I am running into the same error and am not sure what you are talking about.
0
Yavor
Top achievements
Rank 1
answered on 23 Jun 2011, 03:29 PM
Hello,

We're running into a similar problem. The thing is that we should bring certain item of a data-bound treeview into view before the item has been generated. If we try to use ItemContainerGenerator, it returns null. I think the milestone here is to find a way to forcefully generate the item, knowing where it is situated in the hierarchy, provided by the data context.
0
Ammaar
Top achievements
Rank 1
answered on 23 Jun 2011, 05:31 PM
Hi guys,

This is what I ended up doing. Inherit from RadTreeView and expose it's ChangeVisualState method (which is protected):

public class RadTreeViewEx : RadTreeView
{
    public new void ChangeVisualState()
    {
        base.ChangeVisualState();
    }
}

Then in your view-model expose 2 events, one for before your refresh and one for after, e.g.:
private void RefreshTree()
{
    InvokeBeforeTreeRefresh( null );
 
    //  Refresh logic here...
 
    InvokeTreeRefreshedEvent( null );
}
public event EventHandler BeforeTreeRefresh;
 
private void InvokeBeforeTreeRefresh( EventArgs e )
{
    EventHandler handler = BeforeTreeRefresh;
    if ( handler != null ) handler( this, e );
}
 
public event EventHandler TreeRefreshed;
 
private void InvokeTreeRefreshedEvent( EventArgs e )
{
    EventHandler handler = TreeRefreshed;
    if ( handler != null ) handler( this, e );
}

Lastly, in the UserControl where you are using the RadTreeViewEx, you should have something like the following in the codebehind (make sure your RadTreeViewEx has an x:Name property set in the XAML - here I have called it "tree"):

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using Telerik.Windows.Controls;
 
public partial class MyUserControl
{
    public MyUserControl()
    {
        InitializeComponent();
 
        DataContextChanged += MyUserControl_DataContextChanged;
    }
     
    void MyUserControl_DataContextChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
        var viewModel = (MyViewModel) DataContext;
        if (viewModel ==null)
        {
            return;
        }
 
        viewModel.BeforeTreeRefresh += ViewModel_BeforeTreeRefresh;
        viewModel.TreeRefreshed += ItemContainerGenerator_StatusChanged;
    }
 
    private void ViewModel_BeforeTreeRefresh( object sender, EventArgs e )
    {
        tree.IsVirtualizing = false;
        tree.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
    }
 
    void ItemContainerGenerator_StatusChanged( object sender, EventArgs e )
    {
        var itemContainerGenerator = sender as ItemContainerGenerator;
 
        if ( itemContainerGenerator != null && itemContainerGenerator.Status == GeneratorStatus.ContainersGenerated )
        {
            tree.ChangeVisualStatePublic();
 
            itemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
        }
    }
 
}


This is a bit of a hacky solution, but it works for us.
0
Kevin
Top achievements
Rank 1
answered on 27 Sep 2017, 05:46 PM

Problem is that the solution disable virtualization which cause the issue in the first place. 

 

Tags
TreeView
Asked by
Ammaar
Top achievements
Rank 1
Answers by
Hristo
Telerik team
Ammaar
Top achievements
Rank 1
Ristogod
Top achievements
Rank 2
Yavor
Top achievements
Rank 1
Kevin
Top achievements
Rank 1
Share this question
or