2012-09-18 13 views
6

Ho una pagina WPF, con una griglia su di essa.WPF: collapse GridSplitter?

Ci sono tre righe. La riga 0 contiene un GridView con Height="*". La riga 1 contiene un GridSplitter con Height="auto". La riga 2 contiene un modulo dettagli con Height="2*".

Ecco la cosa: ho un pulsante che dovrebbe attivare la visibilità del modulo dettagli. E questo funziona bene. Tranne che nasconde semplicemente il modulo nella riga 2, non espande la griglia nella riga 0 per riempire lo spazio. Quello che voglio è che il pulsante abiliti GridView nella riga 0 per occupare tutto lo spazio, e quindi tornare alla posizione in cui si trovavano le cose.

Chiaramente giocare con la visibilità del modulo all'interno della riga non comporterà ciò che voglio.

Ma con cosa ho bisogno di giocare?

risposta

2

ho dovuto introdurre un Attached proprietà di dipendenza per gestire questo nella mia propria applicazione:

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

prima definire una proprietà allegata adeguato su una 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)); 

Poi ascoltare 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 volta che si guarda per questo e le modifiche, è necessario salvare o ripristinare i valori GridLength dalla riga o colonna che si sta guardando (per brevità sto solo tra cui righe):

// 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; 
     } 
    } 
} 

Tutto ciò che rimane è una adeguata implementazione di GetPrevious e 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

Questo GridExpander controllo che eredita forma GridSpliter che potrebbe per il lavoro che state cercando. Il credito va a Shemesh per aver scritto la versione originale di Silverlight che ho adattato per il mio utilizzo in WPF. Mi trovo a desiderare questa funzionalità quasi ovunque io tenti di usare GridSplitter in modo che possa essere abbastanza utile.

+1

Dovrebbe essere una risposta accettata davvero. Bel lavoro! –

4

Supponiamo che ho questo layout 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> 

allora posso nascondere il secondo controllo (comprimere il divisore verso il basso) con questo codice (equivalente di impostazione Height="0" in XAML):

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

E interromperlo con questo codice (equivalente dell'impostazione Height="1*" in XAML, che è l'impostazione predefinita per una RowDefinition):

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

Questo è ciò che lo splitter fa sotto copertura quando l'utente lo sposta.