C# 用于滚动 TreeView 控件的 WPF ControlTemplate
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/244926/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF ControlTemplate for scrolling TreeView Control
提问by Ben Collins
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.
我正在使用 TreeView 控件,当单击其中一个控件时,它会自动滚动到左对齐 TreeViewItem。我已经查看了我的样式和控制模板,但没有找到任何东西。是否有导致此问题的默认 ControlTemplate?我想禁用它。
采纳答案by brian sharon
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:
项目滚动是因为 ScrollViewer 对它们调用了BringIntoView()。因此,避免滚动的一种方法是禁止处理 RequestBringIntoView 事件。您可以通过继承 TreeView 并实例化此控件来快速尝试:
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();
}
}
回答by Ben Collins
It looks like I found a good clue on MSDN:
看起来我在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.
听起来这是与滚动查看器和焦点系统的交互。
当某个元素在 ScrollViewer(它是 TreeView 模板的一部分)中获得焦点时,会指示 ScrollViewer 使该元素可见。它通过滚动到请求的元素自动响应。
ScrollViewer 内部处理这些焦点请求的方法都是私有的和/或内部的,因此您真的无法访问它们。在这种情况下,我认为您无能为力。这就是焦点的工作原理。
So, is that it? Surely there's a way to modify the TreeView template so that the ScrollViewer won't have this behavior...
那么,是这样吗?当然有一种方法可以修改 TreeView 模板,以便 ScrollViewer 不会有这种行为......
回答by Ben Collins
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...
还在找...
回答by Ben Collins
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.
另一个有趣的花絮:有一个称为 HandlesScrolling 的可覆盖布尔值,它始终返回 true。反编译源代码后,看起来这个属性从未被使用过(或者它被用在 XAML 中某个深层、黑暗、秘密的地方)。我尝试制作自己的 TreeView 控件以将此值设置为 false,但它不起作用。
回答by brian sharon
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
防止 TreeViewItem 上的 RequestBringIntoView 事件冒泡的解决方案是第一步。不幸的是,如果您通过以下方式以编程方式更改所选项目,这也会停止显示树视图项目
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..
希望能帮助到你..