2011-04-07 5 views
21

Implementé una vista de árbol con columnas en WPF usando ControlTemplate y un panel de distribución de GridViewRowPresenter. Seguí este artículo: http://blogs.msdn.com/b/atc_avalon_team/archive/2006/03/01/541206.aspxMantenga la columna más a la izquierda de una vista de árbol visible mientras se desplaza horizontalmente

¡Funciona perfectamente!

Sin embargo, me gustaría mantener visible la columna izquierda (con los nombres) mientras se desplaza horizontalmente.

Sería como 'congelar paneles' en microsoft excel en la primera columna.

Una idea, ¿alguien?

Gracias Frederic

+4

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

+0

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. –

Respuesta

5

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:

  1. Plantillas y estilos para que las líneas coincidan y otros ajustes visuales.
  2. Reintroducción de los aspectos GridView del proyecto vinculado para los encabezados y las columnas.
  3. 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.

+0

+1: Su respuesta deja mucho a la imaginación, incluye errores de sintaxis (por ejemplo 'Establecer propiedad =" Margen "'), y tiene sus limitaciones establecidas. Sin embargo, lo hice funcionar y ** demuestra un enfoque viable para resolver el problema **. ¡Buen trabajo y realmente ganaste la recompensa de este! –

+0

Se solucionó el Setter. Algunas cosas fueron omitidas por el tiempo, algunas porque no eran relevantes. Además de hacer las columnas correctamente (podría hacer eso el fin de semana), ¿hay algo que creas que deba agregarse o no dejarse a la imaginación? –

+0

Proporcionar una respuesta sucinta y consumible siempre es difícil y esta pregunta lo hace doblemente. Cualquiera que ** necesite ** la respuesta a esta pregunta recibirá un toque de rosa con tu respuesta. Pero desde que renuncié a esos puntos de recompensa, agregué un poco de crítica a mi alabanza. ¡Solo mantengan el buen trabajo en futuras preguntas! –

2

¿Sería valioso para concertar otra StackPanel de filas de la izquierda, y entonces de alguna manera enlazar los datos del panel de la izquierda a la primera columna del panel de la derecha? Entonces podrías esconder la primera columna del panel derecho. El panel izquierdo podría tener el tamaño adecuado sin desplazamiento horizontal, y el panel derecho tendría un desplazamiento horizontal normal.

Imagino que el desplazamiento vertical del panel izquierdo de alguna manera tendría que estar vinculado al del panel derecho.

Solo una idea; espero que puedas encontrar una mejor manera.

Cuestiones relacionadas