2011-04-07 47 views
21

我使用ControlTemplate和GridViewRowPresenter的堆栈面板实现了WPF中的列的树状视图。我跟着这篇文章:http://blogs.msdn.com/b/atc_avalon_team/archive/2006/03/01/541206.aspx在水平滚动时保持树形视图的最左侧列可见

它完美的作品!

但是,我想保持左侧列(与名称)水平滚动时可见。

这就像在第一列微软excel上的“冻结窗格”。

一个想法,任何人?

感谢 弗雷德里克

+4

它可以通过将ScrollViewer仅应用于右侧的列,然后将一个不可见的scrollviewer应用到左列并绑定垂直位置来实现。但是需要很多时间来整理代码。 – vorrtex 2011-04-07 22:26:51

+0

不是你想要的答案,但是当我看到你的问题并且可以确认他们的WPF TreeList完全符合你的想法时,我碰巧正在使用DevExpress的WPF演示。实际上,您可以在左侧或右侧修复任意数量的列。即使应付列分类。 – 2011-05-21 06:56:04

回答

5

GridViewRowPresenter解决方案的问题在于该树与其他列不可分割。我想你需要它是分开的,这样你可以将横向排列的ScrollViewer放在列的周围,我怀疑这很容易(如果可能的话)对你链接的文章中的项目做。

这个项目,我打了一些东西出来是相当粗糙的边缘。有几个问题需要分别解决,我没有微调:

  1. 模板和样式,使线匹配,和其他视觉调整。
  2. 重新引入链接项目的GridView方面的标题和列。
  3. 一个分离器,用于调整第一列(包含树)的大小。

像文章项目一样,我用了一棵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中嵌套水平方向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想,我可能会看专业控制第一个花费必要的时间来打磨一个土生土长的解决方案之前。当这种显示的要求很简单时,这种事情会更好。

+0

+1:你的回答留下了很多想象,包括语法错误(例如'Setter Property =“Margin”'),并且有你的限制。尽管如此,我得到了它的工作,它确实证明了解决问题的可行方法。伟大的工作,你真的赢得了这个奖金! – 2011-05-27 01:07:41

+0

修复了Setter。有些事情因时间的缘故而被忽略,有些是因为它们不相关。除了正确地做专栏(周末可能会这样做),有没有什么您认为需要添加或以其他方式留在想象中的? – 2011-05-27 02:03:42

+0

提供一个简洁和可消费的答案总是很难,这个问题使它倍增。任何**需要**这个问题的答案都会在你的答案中变成粉红色。但是,既然我放弃了这些赏金点,我对我的赞美加了一点批评。在未来的问题上继续做好工作! – 2011-05-27 02:12:36

2

难道是值得的安排行的另一StackPanel的左侧,然后以某种方式在左侧面板中的数据绑定到右面板的第一列?然后,您可以隐藏右侧面板的第一列。左侧面板的尺寸可以适当调整而不需要水平滚动,右侧面板可以进行普通的水平滚动。

我会想象左侧面板的垂直滚动将不得不被绑定到右侧面板的垂直滚动。

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

相关问题