WPF Gridsplitter - Sauvegarde et restauration de l'emplacement et de la division proportionnellement

StackOverflow https://stackoverflow.com/questions/5018544

  •  14-11-2019
  •  | 
  •  

Question

Je crée une interface utilisateur de 3 colonnes avec des séparateurs de grille entre les colonnes. J'ai la condition d'enregistrer la satte des colonnes afin que si l'utilisateur ferme et rouvre l'application, il ressemble à ce qu'ils l'ont laissé. Mais j'essaie également d'obtenir les colonnes se diviser proportionnellement - par lesquelles je veux dire si vous étirez la fenêtre, les trois panneaux poussent et si vous déplacez le séparateur gauche, il modifie la division entre les colonnes gauche et centrale.

Qu'est-ce que j'ai actuellement ne réalise que la première exigence - elle enregistre l'état des largeurs de colonne. J'ai également rendu les colonnes appliquent des largeurs minimales pour les trois colonnes. Mais si je comprends bien, la façon de dire à un séparateur de se diviser proportionnellement consiste à utiliser des largeurs de colonne de taille d'étoile. Depuis que j'utilise la propriété de la largeur déjà pour enregistrer et restaurer l'état, je ne suis pas sûr de pouvoir accomplir ce que je veux.

Est-ce que quelqu'un a réussi à enregistrer l'état des largeurs de colonne et que la scission soit proportionnelle?

Voici quelques codes pour ce que j'ai actuellement:

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

J'ai des propriétés de dépendance du type double pour les gauchecolumnMaxWidth et le CentreColumnMaxWidth. Et le gestionnaire de droiteanel_sizechanged ainsi que le gestionnaire chargé de la fenêtre appellent cette méthode:

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

J'ai toujours du travail à faire pour vous assurer que la redimensionnement de la fenêtre ne clipse pas la colonne de droite. Je pense que cette solution peut être liée à la tentative de diviser les séparateurs proportionnellement. Le comportement particulièrement particulier de ma configuration actuelle est que le séparateur gauche redimensionne les colonnes gauche et droite et laisse la taille de la colonne centrale fixée.

Je n'ai pas peur de la manipulation de Sizechanged ou Dragdelta pour atteindre mes objectifs. Mais ce que je crois que je ne peux pas faire est de définir la propriété de largeur des deux premières colonnes, car cela détruirait ma liaison au paramètre utilisateur qui enregistre l'état.

Merci d'avance pour toute aide.

Était-ce utile?

La solution

So I believe I have figured this out. It is possible that some old values in my settings.settings were causing me issues, and it's possible that the default values I put in caused me issues. But here's what I did:

  1. Changed my user settings to save all THREE (not just the left two) column widths. And save them as strings.
  2. Set the default in the user settings (as well as the width property on the columns) to something like 200*.
  3. Set only the MinWidth - not the max - on all three columns.
  4. Manually load and save the user settings for the columns using a GridLengthConverter.

I'm not 100% convinced this is the best way, but it does seem to work, which makes me quite happy. In case anyone else has trouble and comes across this post, here is the working XAML:

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

That size changed event is still there only for debug tracing. I output the values of Width to see what is happening. Curiously, after expanding everything and going back to the window's minimum size, the right column width stays larger. But since they all have minwidths and the widths are all star-sized, it works itself out.

I did try to put this back into a binding, but since I'm now storing a string, and the GridLengthConverter is a TypeConverter, not an IValueConverter, it didn't work. I think it may be possible to store the values as GridLengths, though I've reached a point where I'm happy with what I've done. So my load and save are like this:

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

And that all worked for me. So I'm going to go with it!

EDIT: I later did some refinement to prevent the "curiosity" of the right column staying larger. I now have all panel size changes go to one event handler:

    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);
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top