2012-09-18 18 views
6

Tengo una página WPF, con una cuadrícula.WPF: contraer GridSplitter?

Hay tres filas. La fila 0 contiene un GridView con Height="*". La fila 1 contiene un GridSplitter con Height="auto". La fila 2 contiene un formulario de detalles con Height="2*".

Esto es lo que quiero: tengo un botón que se supone que permite alternar la visibilidad del formulario de detalles. Y eso funciona bien. Excepto que simplemente oculta el formulario en la fila 2, no expande la cuadrícula en la fila 0 para llenar el espacio. Lo que quiero es que el botón para alternar GridView en la fila 0 para ocupar todo el espacio, y luego para volver a donde estaban las cosas.

Claramente jugando con la Visibilidad del formulario dentro de la fila no logrará lo que quiero.

¿Pero con qué tengo que estar jugando?

Respuesta

2

que tuvo que introducir una dependencia de la propiedad adjunta para manejar esto en mi propia aplicación:

<Grid c:GridSplitterController.Watch="{Binding ElementName=GS_DetailsView}"> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="1*" /> 
     <RowDefinition Height="200" /> 
    </Grid.RowDefinitions> 

    <SomeControl Grid.Row="0" /> 

    <GridSplitter x:Name="GS_DetailsView" 
        Height="4" 
        Grid.Row="1" 
        VerticalAlignment="Top" 
        HorizontalAlignment="Stretch" 
        ResizeBehavior="PreviousAndCurrent" 
        ResizeDirection="Rows" 
        Visibility="{Binding ShowDetails, 
             Converter={StaticResource boolvis}}" /> 

    <OtherControl Grid.Row="1" 
        Margin="0,4,0,0" 
        Visibility="{Binding ShowDetails, 
             Converter={StaticResource boolvis}}" /> 
</Grid> 

En primer lugar definir una propiedad adjunta adecuada en un DependencyObject:

public static GridSplitter GetWatch(DependencyObject obj) 
{ 
    return (GridSplitter)obj.GetValue(WatchProperty); 
} 

public static void SetWatch(DependencyObject obj, GridSplitter value) 
{ 
    obj.SetValue(WatchProperty, value); 
} 

public static readonly DependencyProperty WatchProperty = 
    DependencyProperty.RegisterAttached(
     "Watch", 
     typeof(GridSplitter), 
     typeof(DependencyObject), 
     new UIPropertyMetadata(null, OnWatchChanged)); 

Entonces escuche IsVisibleChanged:

private static void OnWatchChanged(DependencyObject obj, 
    DependencyPropertyChangedEventArgs e) 
{ 
    if (obj == null) return; 
    if (obj is Grid) 
    { 
     var grid = obj as Grid; 
     var gs = e.NewValue as GridSplitter; 
     if (gs != null) 
     { 
      gs.IsVisibleChanged += (_sender, _e) => 
       { 
        UpdateGrid(
         grid, 
         (GridSplitter)_sender, 
         (bool)_e.NewValue, 
         (bool)_e.OldValue); 
       }; 
     } 
    } 
} 

Una vez que está viendo para thes E cambia, tiene que guardar o restaurar los GridLength valores de la fila o columnas que se esté viendo (por razones de brevedad sólo estoy incluyendo Filas):

// Given: static Dictionary<DependencyObject, GridLength> oldValues; 
private static void UpdateGrid(Grid grid, GridSplitter gridSplitter, bool newValue, bool oldValue) 
{ 
    if (newValue) 
    { 
     // We're visible again 
     switch (gridSplitter.ResizeDirection) 
     { 
     case GridResizeDirection.Columns: 
      break; 
     case GridResizeDirection.Rows: 
      int ridx = (int)gridSplitter.GetValue(Grid.RowProperty); 
      var prev = grid.RowDefinitions.ElementAt(GetPrevious(gridSplitter, ridx)); 
      var curr = grid.RowDefinitions.ElementAt(GetNext(gridSplitter, ridx)); 
      if (oldValues.ContainsKey(prev) && oldValues.ContainsKey(curr)) 
      { 
       prev.Height = oldValues[prev]; 
       curr.Height = oldValues[curr]; 
      } 

      break; 
     } 
    } 
    else 
    { 
     // We're being hidden 
     switch (gridSplitter.ResizeDirection) 
     { 
     case GridResizeDirection.Columns: 
      break; 
     case GridResizeDirection.Rows: 
      int ridx = (int)gridSplitter.GetValue(Grid.RowProperty); 
      var prev = grid.RowDefinitions.ElementAt(GetPrevious(gridSplitter, ridx)); 
      var curr = grid.RowDefinitions.ElementAt(GetNext(gridSplitter, ridx)); 
      switch (gridSplitter.ResizeBehavior) 
      { 
       // Naively assumes only one type of collapsing! 
       case GridResizeBehavior.PreviousAndCurrent: 
        oldValues[prev] = prev.Height; 
        prev.Height = new GridLength(1.0, GridUnitType.Star); 

        oldValues[curr] = curr.Height; 
        curr.Height = new GridLength(0.0); 
        break; 
      } 
      break; 
     } 
    } 
} 

Todo lo que queda es una aplicación adecuada de GetPrevious y GetNext :

private static int GetPrevious(GridSplitter gridSplitter, int index) 
{ 
    switch (gridSplitter.ResizeBehavior) 
    { 
     case GridResizeBehavior.PreviousAndNext: 
     case GridResizeBehavior.PreviousAndCurrent: 
      return index - 1; 
     case GridResizeBehavior.CurrentAndNext: 
      return index; 
     case GridResizeBehavior.BasedOnAlignment: 
     default: 
      throw new NotSupportedException(); 
    } 
} 

private static int GetNext(GridSplitter gridSplitter, int index) 
{ 
    switch (gridSplitter.ResizeBehavior) 
    { 
     case GridResizeBehavior.PreviousAndCurrent: 
      return index; 
     case GridResizeBehavior.PreviousAndNext: 
     case GridResizeBehavior.CurrentAndNext: 
      return index + 1; 
     case GridResizeBehavior.BasedOnAlignment: 
     default: 
      throw new NotSupportedException(); 
    } 
} 
2

Este GridExpander control que hereda forma GridSpliter que puede ser que con el trabajo que busca. El crédito va al Shemesh para escribir la versión original de Silverlight que he adaptado para mi propio uso en WPF. Me encuentro con ganas de esta funcionalidad en casi todas partes. Intento usar GridSplitter por lo que puede ser bastante útil.

+1

Realmente debería ser una respuesta aceptada. ¡Buen trabajo! –

4

Supongamos que tengo este diseño XAML:

<Grid Name="MyGrid"> 
     <Grid.RowDefinitions> 
      <RowDefinition /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition /> 
     </Grid.RowDefinitions> 
     <MyControl1 ... Grid.Row="0" /> 
     <GridSplitter Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" ShowsPreview="True" Height="5" /> 
     <MyControl2 ... Grid.Row="2" /> 
    </Grid> 

entonces puedo ocultar el segundo control (colapsar el divisor hacia abajo) con el código (equivalente a establecer Height="0" en XAML):

MyGrid.RowDefinitions[2].Height = new GridLength(0); 

Y quítelo con este código (equivalente a la configuración Height="1*" en XAML, que es el valor predeterminado para RowDefinition):

MyGrid.RowDefinitions[2].Height = new GridLength(1, GridUnitType.Star); 

Esto es lo que hace el divisor a los undercovers cuando el usuario lo mueve.

Cuestiones relacionadas