在Silverlight中选择项目时,无法阻止基类中TreeView上的自动水平滚动

时间:2011-10-25 23:42:41

标签: silverlight silverlight-4.0 silverlight-toolkit

当您在Silverlight中选择树视图项时,我试图阻止自动水平滚动。我试图在基类中做到这一点。

到目前为止,我还没有能够管理它。我已经尝试了下面的代码,但它会执行,然后当我选择它时,事情就会滚动。

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;
        }
    }
}

我实际上并不想完全禁用滚动 - 如果项目飞离树视图的边缘,我希望用户能够滚动到它。我想要做的是当我选择一个可能长于屏幕宽度的子节点时,让树保持原样。

我在WrapPanel上尝试过RequestBringIntoView,但似乎只有WPF。我还尝试在UpdateLayout和SelectedItemChanged上执行此操作。一切都无济于事。我似乎无法找到一个通用的解决方案,我可以让另一个类继承并使用。

有人有什么想法吗?

谢谢!

-Ari

编辑:赏金说我需要能够在XAML中完成它。那是我的错字。我也会采用代码解决方案。谢谢!

3 个答案:

答案 0 :(得分:5)

导致ScrollViewer滚动的原因是,每次更改TreeView的选择时,都会调用ScrollIntoView(在TreeView的源代码内) ,

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

它基本上会尝试将标题或整个TreeViewItem滚动到最左侧。我首先想到您可以通过删除所有左TreeView来修改TreeViewItemMargin样式,并使标题与展开按钮的左边距相同。但是,此解决方案仅适用于第二级中的第一级项目,在项目之前有缩进,因此如果单击它将再次滚动。

所以我猜没有纯xaml解决方案。解决这个问题的简单方法是,假设你已经有了自定义控件,而不是调用(我认为存在时间问题,这就是为什么这段代码无效)

_scrollViewer.ScrollToHorizontalOffset(0); 
你做了

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

如果您真的更喜欢xaml解决方案,您可以将此代码放入行为或附加属性中,然后将其放入使用Blend。

说完所有这些之后,我个人认为另一种解决方案更好,并且在我自己的应用程序中使用它是,完全禁用水平滚动条,并对我TextTrimming="WordEllipsis"内的所有TextBlocks TreeViewItems使用GridSplitter指示用户有更多文本。还提供TreeView以允许用户调整{{1}}内容的大小以查看全文而不是水平来回滚动。但这只是我的意见。

希望这有帮助! :)

答案 1 :(得分:0)

我认为在你的情况下,实现这一目标的最佳方法是通过创建一个像这样的简单树视图样式,使树视图中的滚动查看器完全不受影响:

    <Style x:Key="TreeViewStyle1" TargetType="sdk:TreeView">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="sdk:TreeView">
                    <Grid>
                        <ItemsPresenter Margin="5,5,19,19" d:LayoutOverrides="Width, Height"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

然后只需将模板应用到滚动查看器并使用外部滚动查看器将其包装起来,这样:

<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource TreeViewSampleData}}">
    <ScrollViewer>
        <sdk:TreeView ItemsSource="{Binding RootCollection}" ItemTemplate="{StaticResource FirstLevelTemplate}" Style="{StaticResource TreeViewStyle1}"/>
    </ScrollViewer>
</Grid>

现在树视图无法与滚动查看器通信,因此在选择项目时无法滚动。

请记住,这样可以消除任何自动滚动,现在只能进行手动滚动。您仍然可以使用行为或TargetedTriggerAction(s)添加自己的自动滚动。

确保不要在树视图上设置任何大小,以便它在滚动查看器中自由增长。

我希望这会有所帮助。

答案 2 :(得分:0)

对那些对上述解决方案不满意的人的迟到答案:

this MSDN dev blog所述,树视图会滚动,因为TreeViewItem会在选择时调用其BringIntoView方法。 可以通过覆盖其处理程序并将其标记为Handled来取消该事件,如下所示:

XAML:

<TreeView>
  <TreeView.Resources>
    <Style TargetType="TreeViewItem">
      <EventSetter Event="RequestBringIntoView" Handler="TreeViewItem_RequestBringIntoView"/>
    </Style>
  </TreeView.Resources>
</TreeView>

处理程序:

void TreeViewItem_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) {
  e.Handled = true;
}

该方法还有一个缺点:直接调用tvi.BringIntoView()也停止工作,但可以通过在调用BringIntoView之前设置布尔标志,在处理程序中检查它并在调用后重置它来绕过它