El problema con la solución GridViewRowPresenter
es que el árbol es inextricable de las otras columnas. Me imagino que necesita que esté separado para que pueda poner el ScrollViewer
horizontal solamente alrededor de las columnas, y dudo que esto sea fácil (si es posible) para hacer con el proyecto en el artículo que ha vinculado.
Este proyecto que me abofeteé para resolver algo es bastante difícil en los bordes. Hay una serie de problemas que necesitaría resolver por separado que no afine:
- Plantillas y estilos para que las líneas coincidan y otros ajustes visuales.
- Reintroducción de los aspectos
GridView
del proyecto vinculado para los encabezados y las columnas.
- Un divisor para ajustar el tamaño de la primera columna (que contiene el árbol).
Al igual que el proyecto del artículo, utilicé un árbol de Type
objetos como fuente de datos.
El quid de hacer que esto funcione fue envolviendo los objetos de datos en un objeto ExpandingContainer
. Las cosas importantes acerca de esta clase INPC es propiedad IsExpanded
(para la unión) y la colección de los niños:
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) => {};
}
En cuanto a la XAML, en primer lugar vamos a obtener algunos recursos fuera del camino:
<!-- 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>
Originalmente intenté anidar el ScrollViewer
solo horizontal que contiene las columnas de la derecha dentro del ScrollViewer
solo vertical que era responsable del TreeView
, pero eso producía el extraño requisito de que tenía que desplazarse hacia abajo para desplazarse horizontalmente. Así que los separé aún más, colocando el ScrollViewer
uno al lado del otro.
Para mantener la barra de desplazamiento vertical en el extremo derecho, escondí ambas barras de desplazamiento alrededor del TreeView
y utilizo solo las barras de desplazamiento alrededor de las columnas. La sincronización del desplazamiento vertical se realiza en código subyacente, pero para una forma más MVVM de hacerlo, puede realizar un comportamiento adjunto para facilitar el enlace entre sí.
<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>
Y, por último, la parte importante del código subyacente (menos de hacer los objetos de datos y establecer la propiedad DataContext
):
private void columnscroller_ScrollChanged(object sender, ScrollChangedEventArgs e) {
treescroller.ScrollToVerticalOffset(columnscroller.VerticalOffset);
}
creo que sirve, o al menos ofrece una perspectiva diferente.
Si realmente se necesita una buena que llena todas las necesidades que se me ocurrió para un híbrido TreeView
+ ListView
, probablemente me miro controles profesionales primero antes de pasar el tiempo necesario para pulir una solución de cosecha. Este tipo de cosas es mejor cuando los requisitos para dicha visualización son simples.
Se puede lograr aplicando el ScrollViewer solo a las columnas de la derecha, luego aplique un scrollviewer invisible a la columna izquierda y enlace la posición vertical. Pero requerirá mucho tiempo para resolver el código. – vorrtex
No era la respuesta que querías, pero estaba jugando con las demostraciones WPF de DevExpress cuando vi tu pregunta y puedo confirmar que WPF TreeList hace exactamente lo que quieres. De hecho, puede corregir cualquier cantidad de columnas a la izquierda o a la derecha. Incluso hace frente a la clasificación de columnas también. –