WPF GridSplitter - 保存和恢复位置以及按比例分割

12

我正在创建一个有3个列的UI,并在列之间使用网格分隔器。我的要求是保存列的状态,以便用户关闭并重新打开应用程序时,应用程序的外观与他们离开时一样。但我还尝试让这3个列按比例划分 - 也就是说,如果拉伸窗口,则所有三个面板都会增长;如果移动左边的分隔器,它会改变左列和中间列之间的分割。

目前我只实现了第一个要求 - 它保存了列宽的状态。我还为所有三列强制设定了最小宽度。但是据我所知,告诉分隔器按比例划分的方法是使用星号大小的列宽。由于我已经在使用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。右面板大小更改处理程序以及窗口加载处理程序都调用此方法:

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

我还有一些工作要做,以确保调整窗口大小时不会剪切右侧列。我认为这个解决方案可能与尝试使分隔条成比例分割有关。我的当前设置特别奇怪的行为是左分隔条调整左右两列的大小,并使中心列的大小保持不变。

我不怕处理 SizeChanged 或 DragDelta 以实现我的目标。但我相信我不能真正设置前两列的宽度属性,因为那样会破坏我与用户设置保存状态的绑定。

非常感谢任何帮助。

1个回答

8
所以我相信我已经找到了解决方法。我的settings.settings中的一些旧值可能会导致问题,而我放置的默认值也可能会导致问题。但是这是我所做的事情:
  1. 更改我的用户设置以保存所有三个(而不仅仅是左侧两个)列宽度。并将它们保存为字符串。
  2. 在用户设置中设置默认值(以及列的宽度属性)为类似于200*的某些值。
  3. 仅在所有三列上设置MinWidth - 而不是MaxWidth。
  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>

那个大小更改的事件仍然只用于调试跟踪。我输出了宽度的值以查看发生了什么。有趣的是,在展开所有内容并返回窗口的最小大小后,右列的宽度保持较大。但由于它们都有最小宽度,并且宽度都是星号大小,所以问题自然解决了。
我确实尝试将其放回到绑定中,但由于我现在存储的是一个字符串,并且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
        }
    }

这一切对我来说都很有效。所以我会选择它!

编辑:后来,我进行了一些改进,以防止右侧列的“好奇心”保持较大。现在,我所有面板大小更改都通过一个事件处理程序进行:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接