WPF Gridsplitter - Risparmio e ripristino della posizione e della divisione proporzionalmente

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

  •  14-11-2019
  •  | 
  •  

Domanda

Sto creando un'interfaccia utente a 3 colonne con slitteri di griglia tra le colonne. Ho il requisito di salvare il SATE delle colonne in modo che se l'utente chiuda e riapre l'app proprio come se lo avessero lasciato. Ma sto anche cercando di ottenere le colonne da dividere proporzionalmente, con il quale intendo se si allunga la finestra, tutti e tre i pannelli crescono e se si sposta lo splitter sinistro cambia la divisione tra le colonne sinistra e centrale.

Cosa ho attualmente raggiunto solo il primo requisito: salva lo stato delle larghezze della colonna. Ho anche fatto che le colonne impongono le larghezze minime per tutte e tre le colonne. Ma come lo capisco, il modo di dire a uno splitter di dividere proporzionalmente è usare larghezze di colonna di dimensioni stelle. Dal momento che sto usando la proprietà della larghezza già per salvare e ripristinare lo stato, non sono sicuro di poter realizzare ciò che voglio.

Qualcuno è riuscito a salvare entrambi lo stato delle larghezze della colonna e la divisione è proporzionale?

Qui è un codice per quello che sono attualmente:

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

Ho proprietà di dipendenza del tipo di tipo per il doppio di Type sia per sinistraColumnMaxWidth e CenterColumnMaxWidth. E il gestore di destraPanel_SizeChanged e il gestore a caricamento della finestra chiama entrambi questo metodo:

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

Ho ancora un po 'di lavoro da fare per assicurarmi che il ridimensionamento della finestra non raggruppa la colonna giusta. Penso che questa soluzione possa essere correlata a cercare di far slittare le splitter in proporzione. Il comportamento particolarmente peculiare del mio set di corrente è che lo splitter sinistro ridimensiona le colonne sinistro e destro e lascia la dimensione della colonna centrale fissa.

Non ho paura della gestione di SizeChanged o Dragdelta per raggiungere i miei obiettivi. Ma ciò che credo di non poter fare è effettivamente impostare la proprietà di larghezza delle prime due colonne, poiché distruggerebbe il mio legame all'impostazione dell'utente che salva lo stato.

Grazie in anticipo per qualsiasi aiuto.

È stato utile?

Soluzione

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);
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top