2011-02-16 21 views
10

Estoy creando una interfaz de usuario de 3 columnas con divisores de cuadrícula entre las columnas. Tengo el requisito de guardar el estado de las columnas para que, si el usuario cierra y vuelve a abrir la aplicación, se vea como lo dejaron. Pero también estoy tratando de hacer que las columnas se dividan proporcionalmente, con lo que quiero decir que si estira la ventana, los tres paneles crecen y si mueve el divisor izquierdo cambia la división entre las columnas izquierda y central.WPF GridSplitter - guardar y restaurar la ubicación AND dividir proporcionalmente

Lo que tengo actualmente solo alcanza el primer requisito: guarda el estado de los anchos de columna. También hice que las columnas imponen anchuras mínimas para las tres columnas. Pero, según tengo entendido, la forma de decirle a un divisor que se divida proporcionalmente es usar anchos de columna del tamaño de una estrella. Dado que ya estoy usando la propiedad Width para guardar y restaurar el estado, no estoy seguro de poder lograr lo que quiero.

¿Alguien ha logrado guardar el estado de los anchos de columna Y que la división sea proporcional?

Aquí hay un código para lo que tengo actualmente:

<Grid x:Name="mainGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition x:Name="leftColumn" Width="{Binding MainWindowLeftColumnWidth, Mode=TwoWay, Source={x:Static prop:Settings.Default}}" MinWidth="200" MaxWidth="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Path=LeftColumnMaxWidth, Mode=OneWay}"/> 
      <ColumnDefinition Width="Auto"/> 
      <ColumnDefinition x:Name="centerColumn" Width="{Binding MainWindowCenterColumnWidth, Mode=TwoWay, Source={x:Static prop:Settings.Default}}" MinWidth="300" MaxWidth="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Path=CenterColumnMaxWidth, Mode=OneWay}"/> 
      <ColumnDefinition Width="Auto"/> 
      <ColumnDefinition x:Name="rightColumn" Width="*" MinWidth="500"/> 
     </Grid.ColumnDefinitions> 
     <StackPanel x:Name="leftPanel" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0"/> 
     <GridSplitter x:Name="leftSplitter" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Width="5" ResizeDirection="Columns"/> 
     <StackPanel x:Name="centerPanel" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0"/> 
     <GridSplitter x:Name="rightSplitter" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Width="5" ResizeDirection="Columns"/> 
     <StackPanel x:Name="rightPanel" Grid.Column="4" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0" SizeChanged="rightPanel_SizeChanged"/> 
    </Grid> 

tengo propiedades de dependencia de tipo double tanto para LeftColumnMaxWidth y CenterColumnMaxWidth. Y el manejador rightPanel_SizeChanged así como el manejador de ventana Cargado tanto llaman a este método:

private void CalculateMaxWidths() 
    { 
      FrameworkElement content = Content as FrameworkElement; 
      if (content != null) 
      { 
       LeftColumnMaxWidth = content.ActualWidth 
           - leftSplitter.ActualWidth 
           - centerColumn.ActualWidth 
           - rightSplitter.ActualWidth 
           - rightColumn.MinWidth; 
       CenterColumnMaxWidth = content.ActualWidth 
            - leftColumn.ActualWidth 
            - leftSplitter.ActualWidth 
            - rightSplitter.ActualWidth 
            - rightColumn.MinWidth; 
      } 
    } 

todavía tienen mucho trabajo por hacer para asegurarse de que el cambio de tamaño de la ventana no recorta la columna de la derecha. Creo que esa solución puede estar relacionada con tratar de hacer que los divisores se dividan proporcionalmente. El comportamiento particularmente peculiar de mi configuración actual es que el divisor izquierdo cambia el tamaño de las columnas izquierda y derecha, y deja fijo el tamaño de la columna central.

No tengo miedo de manejar SizeChanged o DragDelta para lograr mis objetivos. Pero lo que creo que no puedo hacer es configurar la propiedad Ancho de las dos primeras columnas, ya que eso destruiría mi enlace a la configuración del usuario que guarda el estado.

Gracias de antemano por cualquier ayuda.

Respuesta

6

Así que creo que ya me he dado cuenta. Es posible que algunos valores antiguos en mi settings.settings me estuvieran causando problemas, y es posible que los valores predeterminados que puse me causaron problemas. Pero esto es lo que hice:

  1. Cambié mi configuración de usuario para guardar los TRES anchos de columna (no solo los dos de la izquierda). Y guárdalos como cuerdas.
  2. Establezca el valor predeterminado en la configuración del usuario (así como la propiedad de ancho en las columnas) a algo así como 200 *.
  3. Establezca solo MinWidth - no el máximo - en las tres columnas.
  4. Cargue y guarde manualmente la configuración del usuario para las columnas usando un GridLengthConverter.

No estoy 100% convencido de que esta es la mejor manera, pero parece funcionar, lo que me hace bastante feliz. En caso de que alguien más tiene problemas y viene con este post, aquí está el XAML de trabajo:

<Grid x:Name="mainGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition x:Name="leftColumn" MinWidth="200" Width="200*"/> 
      <ColumnDefinition Width="Auto"/> 
      <ColumnDefinition x:Name="centerColumn" MinWidth="300" Width="300*"/> 
      <ColumnDefinition Width="Auto"/> 
      <ColumnDefinition x:Name="rightColumn" MinWidth="498" Width="498*"/> 
     </Grid.ColumnDefinitions> 
     <StackPanel x:Name="leftPanel" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0"/> 
     <GridSplitter x:Name="leftSplitter" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Width="5" ResizeDirection="Columns"/> 
     <StackPanel x:Name="centerPanel" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0"/> 
     <GridSplitter x:Name="rightSplitter" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Width="5" ResizeDirection="Columns"/> 
     <StackPanel x:Name="rightPanel" Grid.Column="4" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0" SizeChanged="rightPanel_SizeChanged"/> 
    </Grid> 

Que el tamaño del evento modificado todavía está allí sólo para el seguimiento de depuración. Emití los valores de Ancho para ver qué está sucediendo. Curiosamente, después de expandir todo y volver al tamaño mínimo de la ventana, el ancho de la columna derecha se mantiene más grande.Pero dado que todos tienen minwidths y los anchos son todos de tamaño de estrella, se resuelve solo.

Intenté poner esto de nuevo en una encuadernación, pero como ahora estoy almacenando una cadena, y el GridLengthConverter es un TypeConverter, no un IValueConverter, no funcionó. Creo que es posible almacenar los valores como GridLengths, aunque llegué a un punto en el que estoy satisfecho con lo que hice. Así que mi carga y ahorro son los siguientes:

protected override void OnSourceInitialized(EventArgs e) 
    { 
     base.OnSourceInitialized(e); 

     //... 

     try 
     { 
      GridLengthConverter converter = new GridLengthConverter(); 
      leftColumn.Width = (GridLength)converter.ConvertFromString(Settings.Default.MainWindowLeftColumnWidth); 
      centerColumn.Width = (GridLength)converter.ConvertFromString(Settings.Default.MainWindowCenterColumnWidth); 
      rightColumn.Width = (GridLength)converter.ConvertFromString(Settings.Default.MainWindowRightColumnWidth); 

      Trace.WriteLine(string.Format("LOADED Left: {0}, Center: {1}, Right {2}", leftColumn.Width, centerColumn.Width, rightColumn.Width)); 
     } 
     catch (Exception) 
     { 
      // Fail silently, the worse case is we go with the defaults, it's going to be okay 
     } 
    } 

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e) 
    { 
     base.OnClosing(e); 

     //... 

     try 
     { 
      GridLengthConverter converter = new GridLengthConverter(); 
      Settings.Default.MainWindowLeftColumnWidth = converter.ConvertToString(leftColumn.Width); 
      Settings.Default.MainWindowCenterColumnWidth = converter.ConvertToString(centerColumn.Width); 
      Settings.Default.MainWindowRightColumnWidth = converter.ConvertToString(rightColumn.Width); 

      Trace.WriteLine(string.Format("SAVED Left: {0}, Center: {1}, Right {2}", Settings.Default.MainWindowLeftColumnWidth, Settings.Default.MainWindowCenterColumnWidth, Settings.Default.MainWindowRightColumnWidth)); 
     } 
     catch (Exception) 
     { 
      // Fail silently, the worst case is we don't save a little something, it's going to be okay 
     } 
    } 

Y todo eso funcionó para mí. ¡Así que voy a ir con eso!

EDITAR: Más tarde hice un refinamiento para evitar que la "curiosidad" de la columna de la derecha sea más grande. Ahora tengo todos los cambios de tamaño de panel van a un manejador de eventos:

private void PanelSizeChanged(object sender, SizeChangedEventArgs e) 
    { 
     // In order to keep the resizing from losing proportionality, we'll force it to be proportional to the current size. 
     // Otherwise the right panel edges up and up while the other two remain at their starting 200*/300* values. 
     // And when that happens eventually resizing the window only resizes the right panel, not proportionately as it does at the start. 
     leftColumn.Width = new GridLength(leftColumn.ActualWidth, GridUnitType.Star); 
     centerColumn.Width = new GridLength(centerColumn.ActualWidth, GridUnitType.Star); 
     rightColumn.Width = new GridLength(rightColumn.ActualWidth, GridUnitType.Star); 
    } 
Cuestiones relacionadas