WPF GridSplitter-сохранение и восстановление местоположения и пропорциональное разделение

Я создаю 3-столбец UI с разделителями сетки между столбцами. У меня есть требование сохранить количество столбцов, чтобы, если пользователь закрывает и открывает приложение, оно выглядело так же, как они его оставили. Но я также пытаюсь заставить столбцы разделяться пропорционально - я имею в виду, что если вы растянете окно, все три панели вырастут, и если вы переместите левый разделитель, он изменит разделение между левым и центральным столбцами.

чего я в настоящее время достигаю только первое требование-это сохранение состояния ширины столбцов. Я также заставил столбцы обеспечивать минимальную ширину для всех трех столбцов. Но, как я понимаю, способ сказать сплиттеру разделить пропорционально-это использовать ширину столбцов размером со звезду. Поскольку я уже использую свойство Width для сохранения и восстановления состояния, я не уверен, что смогу выполнить то, что хочу.

кому-нибудь удалось сохранить состояние ширины столбцов и разделить их пропорционально?

вот некоторый код для того, что у меня есть в настоящее время:

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

у меня есть свойства зависимостей типа double для LeftColumnMaxWidth и CenterColumnMaxWidth. И обработчик rightPanel_SizeChanged, а также обработчик, загруженный в окно, вызывают этот метод:

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

у меня еще есть работа, чтобы убедиться, что изменение размера окна не обрезает правильный столбец. Я думаю, что решение может быть связано с попыткой сделать splitters разделить пропорционально. Особенно своеобразным поведением моей текущей настройки является то, что левый разделитель изменяет размер левого и правого столбцов и оставляет размер центрального столбца фиксированным.

Я не боюсь обработки SizeChanged или DragDelta для достижения моих целей. Но я считаю, что не могу установить свойство Width первых двух столбцов, так как это уничтожит мою привязку к пользовательскому параметру, который сохраняет состояние.

спасибо заранее для любого помощь.

1 ответов


поэтому я считаю, что я понял это. Возможно, что некоторые старые значения в моих настройках.настройки вызывали у меня проблемы, и возможно, что значения по умолчанию, которые я ввел, вызвали у меня проблемы. Но вот что я сделал:--4-->

  1. изменил настройки пользователя, чтобы сохранить все три (а не только два левых) ширины столбцов. И сохраните их как строки.
  2. установите значение по умолчанию в настройках пользователя (а также свойство width в Столбцах) примерно так 200*.
  3. установите только MinWidth - не max-на всех трех столбцах.
  4. вручную загрузите и сохраните пользовательские настройки для столбцов с помощью GridLengthConverter.

Я не на 100% уверен, что это лучший способ, но он, кажется, работает, что делает меня вполне счастливым. В случае, если кто-то еще имеет проблемы и сталкивается с этим сообщением, вот рабочий 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>

это событие изменения размера все еще существует только для трассировки отладки. I вывод значения ширины, чтобы увидеть, что происходит. Любопытно, что после расширения всего и возврата к минимальному размеру окна ширина правой колонки остается больше. Но так как все они имеют минимальную ширину, а ширина всех звезд, это работает само собой.

Я попытался вернуть это в привязку, но так как теперь я храню строку, а GridLengthConverter-это TypeConverter, а не IValueConverter, это не сработало. Я думаю, что можно сохранить значения как GridLengths, хотя я достиг точки, когда я доволен тем, что я сделал. Поэтому моя нагрузка и сохранение таковы:

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

и все это сработало для меня. Так что я пойду с ним!

EDIT: позже я сделал некоторые уточнения, чтобы предотвратить "любопытство" правой колонки, оставаясь больше. Теперь у меня есть все изменения размера панели, перейдите к одному обработчику событий:

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