在水平滚动时,保持树视图的最左列可见

时间:2011-04-07 18:17:30

标签: c# wpf

我使用ControlTemplate和GridViewRowPresenter的堆栈面板在WPF中实现了一个带有列的树视图。我遵循了这篇文章:http://blogs.msdn.com/b/atc_avalon_team/archive/2006/03/01/541206.aspx

完美无缺!

但是,我希望在水平滚动时保持左列(带名称)可见。

就像在第一列的microsoft excel上'冻结窗格'一样。

一个想法,任何人?

由于 弗雷德里克

2 个答案:

答案 0 :(得分:5)

GridViewRowPresenter解决方案的问题在于树与其他列不可分割。我认为你需要它是独立的,这样你就可以在列中放置仅水平的ScrollViewer,我怀疑这对你链接的文章中的项目很容易(如果可能的话)。

这个项目,我一起打算弄清楚的东西是非常粗糙的边缘。你需要单独解决一些我没有微调的问题:

  1. 模板和样式,以便线条匹配,以及其他视觉调整。
  2. 重新介绍标题和列的链接项目的GridView方面。
  3. 用于调整第一列(包含树)大小的分割器。
  4. 与文章项目一样,我使用Type个对象树作为数据源。

    实现这一目标的关键是将数据对象包装在ExpandingContainer对象中。这个INPC类的重要内容是IsExpanded属性(用于绑定)和子集合:

    public class ExpandingContainer : INotifyPropertyChanged {
        public object Payload { get; private set; }
    
        public ObservableCollection<ExpandingContainer> Children { get; private set; }
    
        public ExpandingContainer( object payload ) { ... }
    
        private bool _isexpanded;
        public bool IsExpanded {
            get { return _isexpanded; }
            set {
                if ( value == _isexpanded )
                    return;
                _isexpanded = value;
                PropertyChanged.Notify( () => IsExpanded );
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged = (o,e) => {};
    }
    

    对于XAML,首先让我们获取一些资源:

    <!-- bind ExpandingContainer.IsExpanded to TreeViewItem.IsExpanded -->
    <Style TargetType="TreeViewItem">
        <Setter Property="IsExpanded"
                Value="{Binding IsExpanded, Mode=TwoWay}" />
    </Style>
    
    <!-- for binding ExpandingContainer.IsExpanded to visibility later -->
    <BooleanToVisibilityConverter x:Key="boolvis" />
    
    <!-- the TreeViewItems should display the Type's name -->
    <HierarchicalDataTemplate DataType="{x:Type loc:ExpandingContainer}"
                              x:Key="treeViewSide"
                              ItemsSource="{Binding Children}">
        <TextBlock Text="{Binding Payload.Name}" />
    </HierarchicalDataTemplate>
    
    <!-- the column side are naively simple, the ItemsControl of children has its
         visibility bound to ExpandingContainer, but the "columns" are just
         StackPanels of TextBlocks -->
    <HierarchicalDataTemplate DataType="{x:Type loc:ExpandingContainer}"
                              x:Key="columnSide">
        <StackPanel>
            <StackPanel.Resources>
                <Style TargetType="TextBlock">
                    <Setter Property="Margin" Value="10,0" />
                </Style>
            </StackPanel.Resources>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Payload.IsAbstract}" />
                <TextBlock Text="{Binding Payload.Namespace}" />
                <TextBlock Text="{Binding Payload.GUID}" />
            </StackPanel>
            <ItemsControl ItemsSource="{Binding Children}"
                          Visibility="{Binding IsExpanded, Converter={StaticResource boolvis}}" />
        </StackPanel>
    </HierarchicalDataTemplate>
    
    <!-- a style can't refer to itself, so this was just to apply it to all ItemsControls -->
    <Style TargetType="ItemsControl">
        <Setter Property="ItemTemplate"
                Value="{StaticResource columnSide}" />
    </Style>
    

    我最初尝试将仅包含右侧列的水平ScrollViewer嵌套在仅ScrollViewer的垂直TreeView内,但这产生了一个奇怪的要求滚动到底部以水平滚动。所以我将它们进一步分开,将ScrollViewer并排放置。

    为了使垂直滚动条保持在最右侧,我将两个滚动条隐藏在TreeView周围,并仅使用列周围的滚动条。同步垂直滚动是在代码隐藏中完成的,但是对于更多MVVM方法,可以创建一个附加行为以便于将它们相互绑定。

    <DockPanel>
        <ScrollViewer VerticalScrollBarVisibility="Hidden"
                      HorizontalScrollBarVisibility="Hidden"
                      DockPanel.Dock="Left"
                      Name="treescroller">
            <TreeView ItemsSource="{Binding Items}"
                      ItemTemplate="{StaticResource treeViewSide}"
                      Padding="0,0,0,20">
            </TreeView>
        </ScrollViewer>
        <ScrollViewer Name="columnscroller"
                      HorizontalScrollBarVisibility="Auto"
                      VerticalScrollBarVisibility="Auto"
                      ScrollChanged="columnscroller_ScrollChanged">
            <ItemsControl ItemsSource="{Binding Items}" />
        </ScrollViewer>
    </DockPanel>
    

    最后,代码隐藏的重要部分(减去制作数据对象并设置DataContext属性):

    private void columnscroller_ScrollChanged( object sender, ScrollChangedEventArgs e ) {
        treescroller.ScrollToVerticalOffset( columnscroller.VerticalOffset );
    }
    

    希望它有所帮助,或者至少提供不同的视角。

    如果我真的需要一个能满足我所能想到的混合TreeView + ListView的所有需求的好人,我可能会先考虑专业控制,然后再花费必要的时间抛光本土解决方案。当这种显示的要求很简单时,这种情况会更好。

答案 1 :(得分:2)

是否值得在左侧安排另一个StackPanel行,然后以某种方式将左侧面板的数据绑定到右侧面板的第一列?然后你可以隐藏右面板的第一列。左侧面板可以在没有水平滚动的情况下适当调整大小,右侧面板可以进行普通的水平滚动。

我认为左侧面板的垂直滚动必须以某种方式绑定到右侧面板的垂直滚动。

只是一个想法;希望你能找到更好的方法。