Exploring WPF - Programmatically scrolling a treeview

Another one of the newest technologies released by Microsoft is WPF (Windows Presentation Foundation). This is a completely new way of thinking about User Interfaces. One of the things that I find the most interesting is the way that complex controls are created in WPF. Let's take a quick look at the TreeView control in WPF.

If you are used to the Windows Forms TreeView, you should probably make a point to forget everything that you know about it. The WPF TreeView is really a composite of other elements (it is not a wrapper around the Win32 TreeView - for better or worse - I think better!). To get the look that you want for a control, WPF provides a Style for the control that is made up of the other simpler elements that give it the appropriate look. The TreeView is visually made up of a Border, then a ScrollViewer, and then an ItemsPresenter. So, I really haven't investigated what an ItemsPresenter is yet, but you should get the idea... the "control" is made up of other elements that deal with the drawing and the control is left to deal with the behavior.

If you want to change the look of a TreeView, just change the style. You can change the border, remove the scroll viewer, or add any other elements you like.

But the real reason I wanted to write this post was to share the code that I had to write to programmatically scroll the treeview. Why would I do that? Well, I was implementing Drag n Drop on this particular TreeView and needed to scroll for the user when they were dragging near the bottom or top of the tree. Because you can change the Style of the TreeView, there isn't a built in way to get to the scrollviewer which really stinks. But, knowing that my control wouldn't change, I was able to create a simple property on my subclasses TreeView to get the scroll viewer and here it is:

        private ScrollViewer ScrollViewer
        {
            get
            {
                if (m_scrollViewer == null)
                {
                    DependencyObject border = VisualTreeHelper.GetChild(this, 0);
                    if (border != null)
                    {
                        m_scrollViewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;
                    }
                }

                return m_scrollViewer;
            }
        }

As you can see, if you know your Visual tree, it is easy to get the ScrollViewer. Once I had the scrollviewer the logic to scroll the tree view was simple as well:

        internal void ScrollIfNeeded(Point mouseLocation)
        {
            if (this.ScrollViewer != null)
            {
                double scrollOffset = 0.0;

                // See if we need to scroll down
                if (this.ScrollViewer.ViewportHeight - mouseLocation.Y < 20.0)
                {
                    scrollOffset = 3.0;
                }
                else if (mouseLocation.Y < 20.0)
                {
                    scrollOffset = -3.0;
                }

                // Scroll the tree down or up
                if (scrollOffset != 0.0)
                {
                    scrollOffset += this.ScrollViewer.VerticalOffset;

                    if (scrollOffset < 0.0)
                    {
                        scrollOffset = 0.0;
                    }
                    else if (scrollOffset > this.ScrollViewer.ScrollableHeight)
                    {
                        scrollOffset = this.ScrollViewer.ScrollableHeight;
                    }

                    this.ScrollViewer.ScrollToVerticalOffset(scrollOffset);
                }
            }
        }

I won't go into a lot of explanation, but if the user is dragging the mouse 20 units (not pixels) from the top of the scroll area or 20 units from the bottom of the scroll area, I simple move the scroll area the appropriate direction by 3 units.

I hope this post helps someone out there trying to do the same sort of thing!