Подтвердить что ты не робот

Держите левый столбец дерева видимым при прокрутке по горизонтали

Я реализовал treeview со столбцами в WPF, используя ControlTemplate и стекную панель GridViewRowPresenter. Я последовал этой статье: http://blogs.msdn.com/b/atc_avalon_team/archive/2006/03/01/541206.aspx

Он отлично работает!

Однако, я бы хотел, чтобы левый столбец (с именами) был виден во время прокрутки по горизонтали.

Это будет как "замораживание окон" на microsoft excel в первом столбце.

Идея, кто-нибудь?

Спасибо Фредерик

4b9b3361

Ответ 1

Проблема с решением 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 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, я бы, наверное, сначала посмотрел на профессиональный контроль, прежде чем тратить необходимое время, чтобы отполировать самодельное решение, Такие вещи лучше, когда требования к такому дисплею просты.

Ответ 2

Было бы целесообразно организовать еще одну StackPanel строк слева, а затем каким-то образом привязать данные левой панели к первому столбцу правой панели? Затем вы можете скрыть первый столбец правой панели. Левая панель может быть настроена соответствующим образом без горизонтальной прокрутки, а правая панель будет иметь обычную горизонтальную прокрутку.

Я бы предположил, что вертикальная прокрутка левой панели каким-то образом должна быть привязана к вертикальной панели.

Просто идея; надеюсь, что вы сможете найти лучший способ.