2
votes

In my MVVM application I have a treeview that should bring a treeviewitem into view on selection. The treeview represents the records in a database. Each treeview item loads its children on demand by expanding the item when it is selected.

The treeview style is defined like this:

  <TreeView.ItemContainerStyle>
    <Style TargetType="{x:Type TreeViewItem}">
      <Setter Property="OverridesDefaultStyle" Value="True"/>
      ...
      <EventSetter Event="Selected" Handler="OnTreeViewItemSelected"/>

The handler is defined like this:

private void OnTreeViewItemSelected(object sender, RoutedEventArgs e)
{
  if (!Object.ReferenceEquals(sender, e.OriginalSource))
  {
    return;
  }
  TreeViewItem item = e.OriginalSource as TreeViewItem;
  if (item != null)
  {        
    EventHandler eventHandler = null;
    eventHandler = new EventHandler(delegate
    {
      treeData.LayoutUpdated -= eventHandler;
      item.BringIntoView();
    });
    treeData.LayoutUpdated += eventHandler;
  }
} 

This works perfectly well for items that are already loaded.

[Edit: in fact the parent of the selected item must be expanded rather than just loaded]

If they are loaded, the treeviewitems are iterated until the sought item is found, the found item is selected and the handler above successfully brings it into view.

The trouble is with items that are not already loaded. In these cases my code gets the ancestor records of the sought item, iterates through them expanding the items as it goes (and therefore loading the children) until it gets to the sought item. This is selected successfully, BUT does not get brought into view.

Does anyone know how to resolve this?

[UPDATE] In TreeViewItemViewModel:

public bool IsSelected
{
  get { return _isSelected; }
  set
  {        
    if (value != _isSelected)
    {          
      _isSelected = value;

      if (value == true)          
        IsExpanded = value;          

      this.OnPropertyChanged("IsSelected");
    }
  }
}

public bool IsExpanded
{
  get { return _isExpanded; }
  set
  {
    if (_isExpanded == value)
      return;

    _isExpanded = value;
    this.OnPropertyChanged("IsExpanded");

    if (_isExpanded && 
        _parent != null &&
        _parent.IsExpanded == false)
      _parent.IsExpanded = true;

    LoadChildren();
  }
}

LoadChildren() method handles whether the children require loading or not by using a flag.

ItemContainerStyle

Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"

2
could you please post more code that is about the IsSelected and IsExpanded properties of the itemcontainerstyle. Also the code that's on the cs side for isselected and isexpanded, and also some code about how you load the subtreeitems and signify that they're loadedMarkus Hütter

2 Answers

3
votes

this might solve your problem

and builds on this article about using MVVM for a TreeView

EDIT: also there's the possibility that your treeviewitemcontainers are not yet created. then you have to fiddle with TreeViews ItemContainerGenerator

0
votes

Ok, so I seem to have resolved this by 'accident'.

If I run the search in a background process the selected it is brought into view

public void Search()
{
  ImplementSearch();

  _workinghelper = new WorkingHelper();
  SearchWorkingViewModel searchvm = new SearchWorkingViewModel(_workinghelper.WorkingWindow);
  VisualUtils.FadeTreeViewItemViewModel(1.0, 0.2, 0, 100);
  _workinghelper.MakeWorking(searchvm);

  VisualUtils.FadeTreeViewItemViewModel(0.2, 1.0, 0, 001);
}

public bool CanSearch
{
  get { return _searchlogic.SearchTypeIsValid(_searchterm); }
}

void ImplementSearch()
{      
  BackgroundTaskManager.RunBackgroundTask(
    () =>
    {
      bool run_success = _searchlogic.PerformSearch(_searchterm);
      Thread.Sleep(500);
      return run_success;
    },
    (run_success) =>
    {
      if (run_success == false)
        ApplicationSetup.DisableApp((MainWindow)App.Current.MainWindow, "The search function has caused an unexpected internal error. Please contact an administrator for help.");

      if (_workinghelper != null)
        _workinghelper.DisposeWorkingWindow();
    });      
}

However, if I simply run bool run_success = _searchlogic.PerformSearch(_searchterm); in ImplementSearch() the item is NOT brought into view.