简体   繁体   中英

WPF ControlTemplate for scrolling TreeView Control

I'm using a the TreeView control and it scrolls automatically to left-align TreeViewItem when one of them is clicked. I've gone looking at my Styles and ControlTemplates, but I haven't found anything. Is there a default ControlTemplate that causes this? I want to disable it.

The items scroll because the ScrollViewer calls BringIntoView() on them. So one way to avoid scrolling is to suppress the handling of the RequestBringIntoView event. You can try that out quickly by subclassing TreeView and instantiating this control instead:

public class NoScrollTreeView : TreeView
{
    public class NoScrollTreeViewItem : TreeViewItem
    {
        public NoScrollTreeViewItem() : base()
        {
            this.RequestBringIntoView += delegate (object sender, RequestBringIntoViewEventArgs e) {
                e.Handled = true;
            };
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new NoScrollTreeViewItem();
        }
    }
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new NoScrollTreeViewItem();
    }
}

after spending some hours on this problem i found a solution that works for me.

brians solution to prevent the RequestBringIntoView event on a TreeViewItem from bubbling was the first step. unfortunately this also stops a treeviewitem to be shown if you change the selected item programmatically by

yourtreeview.SelectedItem = yourtreeviewitem

so, for me the solution is to modify the controltemplate of the treeview as follows:

<Style x:Key="{x:Type TreeView}" TargetType="TreeView">
        <Setter Property="OverridesDefaultStyle" Value="True" />
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TreeView">
                    <Border Name="Border" BorderThickness="0" Padding="0" Margin="1">
                        <ScrollViewer Focusable="False" CanContentScroll="False" Padding="0">
                            <Components:AutoScrollPreventer Margin="0">
                                <ItemsPresenter/>
                            </Components:AutoScrollPreventer>
                        </ScrollViewer>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

the "autoscrollpreventer" is:

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

namespace LiveContext.Designer.GUI.Components {
    public class AutoScrollPreventer : StackPanel
    {
    public AutoScrollPreventer() {

        this.RequestBringIntoView += delegate(object sender, RequestBringIntoViewEventArgs e)
        {
            // stop this event from bubbling so that a scrollviewer doesn't try to BringIntoView..
            e.Handled = true;
        };

    }
}

}

hope it helps..

It looks like I found a good clue on MSDN :

Sounds like this is an interaction with the scrollviewer and the focus system.

When an element is focused within a ScrollViewer (which is part of the TreeView template), the ScrollViewer is instructed to make the element visible. It automatically responds by scrolling to the requested element.

The methods inside of ScrollViewer that handle these focus requests are all private and / or internal so you really can't get to them. I don't think there's too much you can do in this case; it's just how focus works.

So, is that it? Surely there's a way to modify the TreeView template so that the ScrollViewer won't have this behavior...

Ok, I was finally able to get the default style like this:

        using (Stream sw = File.Open(@"C:\TreeViewDefaults.xaml", FileMode.Truncate, FileAccess.Write))
        {
            Style ts = Application.Current.FindResource(typeof(TreeView)) as Style;
            if (ts != null)
                XamlWriter.Save(ts, sw);
        }

Which produced:

<Style TargetType="TreeView" 
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:s="clr-namespace:System;assembly=mscorlib" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style.Triggers>
        <Trigger Property="VirtualizingStackPanel.IsVirtualizing">
            <Setter Property="ItemsControl.ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate><VirtualizingStackPanel IsItemsHost="True" /></ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
            <Trigger.Value>
                <s:Boolean>True</s:Boolean>
            </Trigger.Value>
        </Trigger>
    </Style.Triggers>
    <Style.Resources>
        <ResourceDictionary />
    </Style.Resources>
    <Setter Property="Panel.Background">
        <Setter.Value><DynamicResource ResourceKey="{x:Static SystemColors.WindowBrushKey}" /></Setter.Value>
    </Setter>
    <Setter Property="Border.BorderBrush">
        <Setter.Value><SolidColorBrush>#FF828790</SolidColorBrush></Setter.Value>
    </Setter>
    <Setter Property="Border.BorderThickness">
        <Setter.Value><Thickness>1,1,1,1</Thickness></Setter.Value>
    </Setter>
    <Setter Property="Control.Padding">
        <Setter.Value><Thickness>1,1,1,1</Thickness></Setter.Value>
    </Setter>
    <Setter Property="TextElement.Foreground">
        <Setter.Value><DynamicResource ResourceKey="{x:Static SystemColors.ControlTextBrushKey}" /></Setter.Value>
    </Setter>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility">
        <Setter.Value><x:Static Member="ScrollBarVisibility.Auto" /></Setter.Value>
    </Setter>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility">
        <Setter.Value><x:Static Member="ScrollBarVisibility.Auto" /></Setter.Value>
    </Setter>
    <Setter Property="Control.VerticalContentAlignment">
        <Setter.Value><x:Static Member="VerticalAlignment.Center" /></Setter.Value>
    </Setter>
    <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate TargetType="TreeView">
                <Border BorderThickness="{TemplateBinding Border.BorderThickness}" 
                        BorderBrush="{TemplateBinding Border.BorderBrush}" 
                        Name="Bd" SnapsToDevicePixels="True">
                    <ScrollViewer CanContentScroll="False" 
                                  HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" 
                                  VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" 
                                  Background="{TemplateBinding Panel.Background}" 
                                  Padding="{TemplateBinding Control.Padding}" 
                                  Name="_tv_scrollviewer_" 
                                  SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" 
                                  Focusable="False">
                        <ItemsPresenter />
                    </ScrollViewer>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="UIElement.IsEnabled">
                        <Setter Property="Panel.Background" TargetName="Bd">
                            <Setter.Value>
                                <DynamicResource ResourceKey="{x:Static SystemColors.ControlBrushKey}" />
                            </Setter.Value>
                        </Setter>
                        <Trigger.Value>
                            <s:Boolean>False</s:Boolean>
                        </Trigger.Value>
                    </Trigger>
                    <Trigger Property="VirtualizingStackPanel.IsVirtualizing">
                        <Setter Property="ScrollViewer.CanContentScroll" TargetName="_tv_scrollviewer_">
                            <Setter.Value><s:Boolean>True</s:Boolean></Setter.Value>
                        </Setter>
                        <Trigger.Value>
                            <s:Boolean>True</s:Boolean>
                        </Trigger.Value>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Which, unfortunately, doesn't look helpful. I don't see any properties in there for stopping the auto-scroll-focus thing.

Still looking...

Another fun tidbit: there is a overridable boolean value called HandlesScrolling that always returns true. After decompiling the source, it looks like this property is NEVER used (or it's being used in some deep, dark, secret place in XAML). I tried making my own TreeView control to set this value to false and it didn't work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM