【问题标题】:Keep the leftmost column of a treeview visible while scrolling horizontally水平滚动时保持树视图的最左侧列可见
【发布时间】:2011-04-07 18:17:29
【问题描述】:

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

效果很好!

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

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

一个想法,任何人?

谢谢 弗雷德里克

【问题讨论】:

  • 可以通过只对右边的列应用ScrollViewer,然后在左边的列应用一个不可见的scrollviewer并绑定垂直位置来实现。但是整理代码需要很多时间。
  • 不是您想要的答案,但是当我看到您的问题并且可以确认他们的 WPF TreeList 完全符合您的要求时,我恰好正在玩 DevExpress 的 WPF 演示。事实上,您可以将任意数量的列固定在左侧或右侧。甚至还可以处理列排序。

标签: c# wpf


【解决方案1】:

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 嵌套在负责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 的所有需求的好控制器,我可能会先看看专业控制,然后再花必要的时间来打磨房屋-成长的解决方案。这种显示的要求比较简单的时候这种东西比较好。

【讨论】:

  • +1:您的答案留下了很多想象空间,包括语法错误(例如Setter Property="Margin"),并且有您声明的限制。尽管如此,我还是让它发挥了作用,它确实展示了解决问题的可行方法。伟大的工作,你真的赢得了这个赏金!
  • 修复了 Setter。有些东西是因为时间原因被省略了,有些是因为它们不相关。除了正确地做专栏(周末可能会做)之外,还有什么你认为需要添加或留给想象的吗?
  • 提供简洁易懂的答案总是很困难,而这个问题更是加倍。任何需要回答这个问题的人都会被你的回答逗乐。但由于我放弃了那些赏金,我对我的赞美加了一点批评。继续努力解决未来的问题!
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-14
    • 2015-09-26
    • 2022-01-01
    相关资源
    最近更新 更多