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