12

列間にグリッド スプリッターがある 3 列の UI を作成しています。列の状態を保存して、ユーザーがアプリを閉じて再度開いた場合に、アプリを離れたように見えるようにする必要があります。しかし、列を比例して分割しようとしています。つまり、ウィンドウを引き伸ばすと、3 つのパネルすべてが拡大し、左側のスプリッターを移動すると、左側と中央の列の間の分割が変更されます。

私が現在持っているものは、最初の要件のみを達成しています-列幅の状態を保存します。また、列が 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>

LeftColumnMaxWidth と CenterColumnMaxWidth の両方に double 型の依存関係プロパティがあります。そして、rightPanel_SizeChanged ハンドラーとウィンドウ Loaded ハンドラーの両方がこのメソッドを呼び出します。

    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 を処理することを恐れていません。しかし、私ができないと思うのは、最初の 2 つの列の Width プロパティを実際に設定することです。これは、状態を保存するユーザー設定へのバインディングを破壊するからです。

よろしくお願いします。

4

1 に答える 1

8

だから私はこれを理解したと信じています。私のsettings.settingsの古い値が問題を引き起こしている可能性があり、入力したデフォルト値が問題を引き起こした可能性があります。しかし、ここに私がしたことがあります:

  1. ユーザー設定を変更して、3 つすべて (左の 2 つだけでなく) の列幅を保存しました。そしてそれらを文字列として保存します。
  2. ユーザー設定 (および列の幅プロパティ) のデフォルトを 200* などに設定します。
  3. 3 つの列すべてで、最大ではなく、最小幅のみを設定します。
  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 は IValueConverter ではなく TypeConverter であるため、機能しませんでした。値を 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
        }
    }

そして、それはすべて私にとってうまくいきました。だから私はそれで行くつもりです!

編集:私は後で、右側の列の「好奇心」が大きくなるのを防ぐために、いくつかの改良を行いました。これで、すべてのパネル サイズの変更が 1 つのイベント ハンドラーに送られるようになりました。

    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);
    }
于 2011-02-17T16:17:47.533 に答える