silverlightsilverlight-4.0silverlight-toolkit

Unable to prevent automatic horizontal scrolling on TreeView in base class when selecting an item in Silverlight


I'm trying to prevent the automatic horizontal scrolling on a tree view item when you select it in Silverlight. And I'm trying to do it in a base class.

So far I haven't been able to manage it. I have tried the following code, but it executes and then the thing just scrolls anyway when I select it.

using System.Windows;
using System.Windows.Controls;

namespace MyControls
{
    public class CustomTreeView : TreeView
    {
        private ScrollViewer _scrollViewer;

        protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
        {
            base.OnSelectedItemChanged(e);
            _scrollViewer.ScrollToHorizontalOffset(0);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _scrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer;
        }
    }
}

I don't actually want to disable scrolling entirely -- if an item flies off the edge of the tree view I want the user to be able to scroll to it. What I am trying to do is to keep the tree where it is when I select a child node that may be longer than the width of the screen.

I've tried RequestBringIntoView on a WrapPanel, but that's WPF-only, it seems. I've also tried doing this on UpdateLayout as well as SelectedItemChanged. All to no avail. I can't seem to find a general solution that I can have another class inherit from and use.

Anyone have any ideas?

Thanks!

-Ari

EDIT: The bounty states that I need to be able to do it in XAML. That was a typo on my part. I'll take code solutions as well. Thanks!


Solution

  • What causes the ScrollViewer to scroll is because every time the selection of the TreeView is changed, a ScrollIntoView is called (inside the TreeView's source code),

    this.ItemsControlHelper.ScrollIntoView(container.HeaderElement ?? container);
    

    and it basically tries to scroll either the header or the entire TreeViewItem to the very left. I first thought you could modify both TreeView and TreeViewItem styles by removing all the left Margin and make the header the same left margin as the expander button. However, this solution only works for first level items as in the second level, there's an indentation before the item so if you click on it it will scroll again.

    So I guess there is no pure xaml solution. The easist way to solve this issue would be, given that you've already got the custom control, instead of calling (I think there's a timing issue and that's why this code is not working)

    _scrollViewer.ScrollToHorizontalOffset(0); 
    

    you do

    var scrollableRegions = _scrollViewer.GetVisualDescendants().OfType<IScrollInfo>();
                foreach (var region in scrollableRegions)
                {
                    region.SetHorizontalOffset(0);
                }
    

    If you really prefer xaml solutions you may as well put this code into a behavior or an attached property, and just drop it in using Blend.

    Having said all these, another solution I personally think is better and have used it in my own appliation is, disable the horizontal scrollbar entirely, and use TextTrimming="WordEllipsis" for all my TextBlocks inside TreeViewItems to indicate the user there's more text. Also provide a GridSplitter to allow the user to resize the TreeView content to see full text instead of scrolling back and forth horizontally. But this is just my opinion.

    Hope this helps! :)