10

I want the user to be able to search for Items in a TreeView. After entering a searchtext the TreeViewItem should be scrolled into view.

Right now I am using the MVVM Pattern for the TreeView, for the TreeViewItems and the MainView.

What do I have to do to get the functionality of BringIntoView utilizing MVVM? Is there some property I can bind? (in MFC there was something like FirstVisibileItem)

Have seen a "solution" with a behavior. Is it really necessary?

Mare Infinitus
  • 8,024
  • 8
  • 64
  • 113
  • I think MVVM should stop at the concept of "selected". If that doesn't scroll the item into view, I would use regular events in the View. – H H Mar 23 '13 at 13:03
  • 1
    You are right, the viewmodel should not care about the view. But here is some special case which is "at the borders" of MVVM I believe. – Mare Infinitus Mar 23 '13 at 13:57
  • The VM should indicate a certain item, but that's it. Scrolling issues are better dealt with in the View. – H H Mar 23 '13 at 14:25
  • okay, I believe I set an "ScrollToItem" in my viewmodel and then call BringToView in Codebehind in some handler? Or what do you think? That sound like a solution and it sticks to MVVM, but sounds somewhat complicated for an issue that should be quite easy and quite common. – Mare Infinitus Mar 23 '13 at 14:34
  • Yes, a behavior is necessary, but no need to feel disgruntled. I think behaviors are the perfect complement to View and ViewModel, and the sooner you learn to like them, the better. I would have wanted to post a solution with a behavior, but since you found one already, I will only try to implement it if that doesn't work for you. – Mike Fuchs Mar 23 '13 at 18:02
  • the one I found is based on MVVMLight and I don't want to use that. If you have a good link how to achieve BringintoView via Behavior I will accept it as answer to. EDIT: found this one here, looks good http://www.codeproject.com/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF – Mare Infinitus Mar 23 '13 at 18:18

2 Answers2

12

According to the mentioned Code Project article, here is the Code example that shows how to setup the Behavior and how to integrate the Behavior in XAML.

Setup the behavior:

/// <summary>
/// Exposes attached behaviors that can be
/// applied to TreeViewItem objects.
/// </summary>
public static class TreeViewItemBehavior
{
    #region IsBroughtIntoViewWhenSelected
    public static bool GetIsBroughtIntoViewWhenSelected(TreeViewItem treeViewItem)
    {
        return (bool)treeViewItem.GetValue(IsBroughtIntoViewWhenSelectedProperty);
    }

    public static void SetIsBroughtIntoViewWhenSelected(      TreeViewItem treeViewItem, bool value)
   {
       treeViewItem.SetValue(IsBroughtIntoViewWhenSelectedProperty, value);
   }

   public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty =
    DependencyProperty.RegisterAttached(
    "IsBroughtIntoViewWhenSelected",
    typeof(bool),
    typeof(TreeViewItemBehavior),
    new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged));

    static void OnIsBroughtIntoViewWhenSelectedChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        TreeViewItem item = depObj as TreeViewItem;
        if (item == null)
           return;

        if (e.NewValue is bool == false)
           return;

        if ((bool)e.NewValue)
           item.Selected += OnTreeViewItemSelected;
        else
           item.Selected -= OnTreeViewItemSelected;
    }

    static void OnTreeViewItemSelected(object sender, RoutedEventArgs e)
    {
       // Only react to the Selected event raised by the TreeViewItem
       // whose IsSelected property was modified. Ignore all ancestors
       // who are merely reporting that a descendant's Selected fired.
       if (!Object.ReferenceEquals(sender, e.OriginalSource))
         return;

       TreeViewItem item = e.OriginalSource as TreeViewItem;
       if (item != null)
          item.BringIntoView();
    }

    #endregion // IsBroughtIntoViewWhenSelected
}

Then integrate the TreeViewItemBehavior in XAML:

<TreeView.ItemContainerStyle>
  <Style TargetType="{x:Type TreeViewItem}">
    <Setter Property="local:TreeViewItemBehavior.IsBroughtIntoViewWhenSelected" Value="True"/>
  </Style>
</TreeView.ItemContainerStyle>

Have fun :-)

kbisang
  • 558
  • 4
  • 13
  • The setter for the dependency property is not applied to the items in my treeview until after they are made visible by expanding them. Therefore, TreeViewItem.Selected does not have an event attached initially. If I have code that programmatically expands nodes and selects one deep in the tree, it is not brought into view. – DannyMeister May 25 '16 at 19:32
  • Found a resolution for my specific case. If the dependency property is being enabled, then it made sense for me to go ahead and scroll to the item if it was already selected. Inside the if, after the line: `item.Selected += OnTreeViewItemSelected;` I added: `if (item.IsSelected){ item.RaiseEvent(new RoutedEventArgs(TreeViewItem.SelectedEvent)); }` – DannyMeister May 25 '16 at 21:36
  • That's not a behavior, that's an extension. TreeViewItem*Behavior* – Snicker Sep 07 '16 at 15:55
4

The problem can be solved with an Behavior.

This CodeProject article describes it very good and it works out of the box.

Mare Infinitus
  • 8,024
  • 8
  • 64
  • 113