2011-02-16 48 views
10

我正在用列间的网格分隔符创建3列UI。我有要求保存列的状态,以便如果用户关闭并重新打开应用程序,它看起来就像他们离开它。但我也试图让列按比例分割 - 我的意思是,如果您拉伸窗口,所有三个面板都会增长,如果您移动左侧分割线,它将更改左侧和中间列之间的分割。WPF GridSplitter - 保存和恢复位置并按比例分割

我目前只实现了第一个要求 - 它节省了列宽的状态。我也使这些列强制所有三列的最小宽度。但据我了解,分配器按比例分配的方式是使用星号大小的列宽。由于我正在使用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> 

我有双型两种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; 
      } 
    } 

我还有一些工作要做,以确保调整窗口的大小不夹右侧立柱。我认为这个解决方案可能与试图让分离器按比例分配有关。我目前设置的特别特殊的行为是,左分离器调整左右列的大小,并固定中心列的大小。

我不害怕处理SizeChanged或DragDelta来实现我的目标。但是我相信我不能做的事情实际上是设置前两列的Width属性,因为这会破坏我绑定到保存状态的用户设置。

非常感谢您的帮助。

回答

6

所以我相信我已经想通了。我的settings.settings中的一些旧值可能会导致我遇到问题,并且可能我放入的默认值导致了问题。但这是我做了什么:

  1. 更改我的用户设置以保存所有THREE(不只是左侧两列)的宽度。并将它们保存为字符串。
  2. 将用户设置中的默认值(以及列上的宽度属性)设置为200 *。
  3. 在所有三列上只设置MinWidth - 不是最大值。
  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> 

该尺寸变化的情况下仍然有只进行调试跟踪。我输出Width的值来看看发生了什么。奇怪的是,在扩展所有内容并返回到窗口的最小尺寸后,右列宽度保持较大。但是由于它们都具有最小宽度并且宽度都是明星大小的,所以它自行解决。

我确实试图把它放回到一个绑定中,但由于我现在正在存储一个字符串,而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); 
    }