BringIntoView for MVVM?

5 posts, 1 answers
  1. Bob
    Bob avatar
    59 posts
    Member since:
    Aug 2010

    Posted 20 Dec 2011 Link to this post

    Hello,

    I have a RadTreeView using a templated ItemsSource bound to a node collection in the view model.  I have implemented a search on the tree, and the found node is programmatically selected within the view model (there is no code in the view).  Do you have an example of how to bring the selected node into view using this type of MVVM structure?

    Thanks for any help.
  2. Bob
    Bob avatar
    59 posts
    Member since:
    Aug 2010

    Posted 20 Dec 2011 Link to this post

    I believe I have found the answer.  Using Josh Smith's behavior as a starting point (http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx), I modified to reference RadTreeViewItem and called BringItemIntoView.  This seems to work so far.

    Edit:  Still having a problem with collapsed items.  I try calling expand on all nodes in the path before selecting and bringing into view, but the item is not within view when all is done.
  3. UI for WPF is Visual Studio 2017 Ready
  4. Bob
    Bob avatar
    59 posts
    Member since:
    Aug 2010

    Posted 22 Dec 2011 Link to this post

    Does anyone have a suggestion for how to ensure that a collapsed item is fully expanded before BringItemIntoView is called?

    When the tree is first loaded and not expanded, BringItemIntoView does not work on an item that is within a collapsed node (I assume this is an item that is not "immediate").  However, if the item is expanded into view at least once, then BringItemIntoView will work after that.  I am trying to figure out a way to first expand the path before programmatically selecting the item into view, but no matter what I try, the item does not scroll into view.  Is this because the expanding is done on some type of delay?
  5. Answer
    Hristo
    Admin
    Hristo avatar
    352 posts

    Posted 23 Dec 2011 Link to this post

    Hello Bob,

    TreeView creates its visual items (TreeViewItem containers) as lazy as possible. Thus when item has not been expanded its children will not be created. Calling bring into view for container that is not yet created will have no effect.
    As you have noticed, expanding item will force its children to be created (in case the virtualization is switched off). However, expanding item programmatically is asynchronous operation, i.e. function call returns but containers are generated at some later point in time.
    I would suggest you take a look at this blogpost http://blogs.telerik.com/xamlteam/posts/11-01-12/treeview-bringintoview.aspx . It should work for and fit in your case.

    Greetings,
    Hristo
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  6. Bob
    Bob avatar
    59 posts
    Member since:
    Aug 2010

    Posted 23 Dec 2011 Link to this post

    Thank you Hristo, this helped me solve the problem. 

    Just so everyone is aware, the method presented by Josh Smith in my post above will not work with the RadTreeView.  This is because the items of the tree that are initially collapsed are not actually instantiated, so therefore the selected event will never be registered.  To work around this, I used a GalaSoft MVVM messenger event instead, and handled the message in a behavior that brings the item into view.  Here is the general code I used.

    First, I created a behavior in the View namespace:
    public class TreeSelectionBehavior : Behavior<FrameworkElement>
    {
        Messenger messenger = Messenger.Default;
     
        protected override void OnAttached()
        {
            base.OnAttached();
     
            messenger.Register<GalaSoft.MvvmLight.Messaging.DialogMessage>(this, Identifier, ItemSelected);
        }
     
        public string Identifier { get; set; }
     
        public MyTreeGenericNode SelectedNode
        {
            get { return (MyTreeGenericNode)GetValue(SelectedNodeProperty); }
            set { SetValue(SelectedNodeProperty, value); }
        }
     
        // Using a DependencyProperty as the backing store for the selected node object.
        public static readonly DependencyProperty SelectedNodeProperty =
            DependencyProperty.Register("SelectedNode", typeof(MyTreeGenericNode), typeof(TreeSelectionBehavior), new UIPropertyMetadata(null));
     
        public RadTreeView TreeView
        {
            get { return (RadTreeView)GetValue(TreeViewProperty); }
            set { SetValue(TreeViewProperty, value); }
        }
     
        // Using a DependencyProperty as the backing store for the RadTreeView.
        public static readonly DependencyProperty TreeViewProperty =
            DependencyProperty.Register("TreeView", typeof(RadTreeView), typeof(TreeSelectionBehavior), new UIPropertyMetadata(null));
     
        public void ItemSelected(GalaSoft.MvvmLight.Messaging.DialogMessage dm)
        {
            Assert.IsNotNull(SelectedNode);
            Assert.IsNotNull(TreeView);
     
            TreeView.BringPathIntoView(SelectedNode.DisplayPath);
        }
    }

    The behavior is added to the XAML and bound to the selected item and tree.  The reference to i: is for the System.Windows.Interactivity namespace.  The reference to v: is for the View namespace:

    <i:Interaction.Behaviors>
        <v:TreeSelectionBehavior x:Name="MyTreeSelectionBehavior" Identifier="TreeSelectionBehavior"
                             TreeView="{Binding ElementName=MyRadTreeView}" SelectedNode="{Binding CurrentSelectedItem}" />
    </i:Interaction.Behaviors>

    Now, in the view model, I message the behavior when the selection is updated:

    CurrentSelectedItem = NodeFoundFromSearch;
     
    GalaSoft.MvvmLight.Messaging.Messenger mes = GalaSoft.MvvmLight.Messaging.Messenger.Default;
    mes.Send(new GalaSoft.MvvmLight.Messaging.DialogMessage("Tree Selection Behavior", res =>
    {
    }),
        "TreeSelectionBehavior");

    Make sure to unregister the message in the cleanup of the view:
    messenger.Unregister<GalaSoft.MvvmLight.Messaging.DialogMessage>(MyTreeSelectionBehavior);

    One final note - the node class that is used as items source must have a ToString() override that represents the name of the path element for the node:
    public override string ToString()
    {
        return this.Name;
    }

Back to Top
UI for WPF is Visual Studio 2017 Ready