GridViewRowPresenter 解决方案的问题是树与其他列密不可分。我认为您需要将其分开,以便您可以将仅水平的 ScrollViewer 放在列周围,我怀疑这对您链接的文章中的项目很容易(如果可能的话)。
我拼凑起来想弄清楚的这个项目的边缘相当粗糙。您需要单独解决一些我没有微调的问题:
- 使线条匹配的模板和样式,以及其他视觉调整。
- 重新引入标题和列的链接项目的
GridView 方面。
- 用于调整第一列(包含树)大小的拆分器。
与文章项目一样,我使用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 嵌套在负责TreeView 的仅垂直的ScrollViewer 内包含右列,但这产生了一个奇怪的要求,即您必须滚动到底部才能水平滚动。所以我将它们进一步分开,将ScrollViewers 并排放置。
为了使垂直滚动条保持在最右侧,我将两个滚动条都隐藏在 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 的所有需求的好控制器,我可能会先看看专业控制,然后再花必要的时间来打磨房屋-成长的解决方案。这种显示的要求比较简单的时候这种东西比较好。