2011-07-21 4 views
1

私のシナリオでは、エンドユーザーは、ユーザーインターフェイスを行に分割し、それらの行の高さルールを定義することでカスタマイズしました(固定、スペース、コンテンツに合わせる)。私はWPF Gridを使ってこれを実装します。RowHeight = Autoがグリッドを伸ばさないようにしますか?

グリッドが画面全体を塗りつぶし始め、それ以上大きくならないようにしてください。グリッド全体を常に見ることができなければなりません(グリッド全体のスクロールバーは大丈夫ですが、スクロールバーはありません)。

私の問題の要点:ユーザーが1つ以上の「自動」サイズの行を作成すると、それらの行のコンテンツによってグリッド全体のサイズが拡大され、スクロールバーが導入されます。グリッドの最大高さを固定数に設定します。

一度グリッドを伸ばすと、その星サイズの行が空き領域を埋めるので、自動サイズの行が後で縮小されてもグリッドは永久に伸びるため、星型サイズの行が含まれると悪化します。

"auto"行を必要に応じて拡大/縮小するように制限する方法を見つける必要がありますが、すべての行の実際の高さの合計がグリッド全体より大きくなることはありません。

説明すると、このグリッドは固定された最大高さで、すべてのサイズモードを表す行があります。この例では

<Grid.RowDefinitions> 
    <RowDefinition Height="Auto"/> 
    <RowDefinition Height="Auto"/> 
    <RowDefinition Height="200"/> 
    <RowDefinition Height="*"/> 
</Grid.RowDefinitions> 

<ScrollViewer VerticalScrollBarVisibility="Auto"> 
    <TextBlock> 
     abc<LineBreak/> 
     abc<LineBreak/> 
     abc<LineBreak/> 
     abc<LineBreak/> 
     abc<LineBreak/>      
    </TextBlock> 
</ScrollViewer> 

、「ABC」のテキストブロック膨張すると、グリッドの全体の高さは、固定された「300」の最大高さを超えて広がっています。自動サイズの行の柔軟性を保ちながら、この動作がグリッドの最大高さを保証するにはどうすればよいですか?

答えて

0

さて、Gridのサブクラスを作成して、Measure()とArrange()のレイアウトステップをオーバーライドする必要があることを発見しました。

これは大きな汎用ソリューションであるとは言いませんが、私のシナリオではうまくいきます。私の場合、1つの列しかないので、特に私は列を扱っていないことに注意してください。私はまた、セル内に要素を配置していません(私はそれらを左上に固定したままにしています)。

もっと一般的な解決策が必要な場合は、これは非常に良いスタートだと思います。列の問題は行の問題と同じですが、もう一方の方向にあります。

class NoStretchGrid:Grid 
{ 
    //this override determines what size we ask to be 
    //gotta make sure we never ask for more than the max height 
    protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) 
    { 
     //what would a basic Grid do? 
     System.Windows.Size desiredSize = base.MeasureOverride(constraint); 

     if (desiredSize.Height > constraint.Height) 
      desiredSize.Height = constraint.Height; 

     //if max height is defined and desired height is too big, reduce it 
     if (this.MaxHeight != double.NaN && desiredSize.Height > this.MaxHeight) 
     { 
      desiredSize.Height = this.MaxHeight; 
     } 

     return desiredSize; 
    } 

    //this override tells child controls how big they can be and where they're positioned 
    protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize) 
    { 
     //must decide how tall each row will be 
     double[] desiredHeights = new double[this.RowDefinitions.Count]; 
     double[] minimumHeights = new double[this.RowDefinitions.Count]; 
     double[] finalHeights = new double[this.RowDefinitions.Count]; 

     //first, find out how tall each row wants to be 

     //check for fixed-size rows 
     for (int i = 0; i < desiredHeights.Length; i++) 
     { 
      if (this.RowDefinitions[i].Height.IsAbsolute) 
      { 
       desiredHeights[i] = this.RowDefinitions[i].Height.Value; 
      } 
      else 
      { 
       desiredHeights[i] = 0; 
      } 

      minimumHeights[i] = this.RowDefinitions[i].MinHeight; 
     } 

     //then ask children how big they want to be 
     foreach (UIElement child in this.InternalChildren) 
     { 
      int row = Grid.GetRow(child); 
      if (!this.RowDefinitions[row].Height.IsAbsolute && child.DesiredSize.Height > desiredHeights[row]) 
      { 
       desiredHeights[row] = child.DesiredSize.Height; 
      } 

      if ((child as FrameworkElement).MinHeight > minimumHeights[row]) 
      { 
       minimumHeights[row] = (child as FrameworkElement).MinHeight; 
      } 
     } 

     double availableHeight = arrangeSize.Height; 

     //reserve minimum heights 
     for (int i = 0; i < minimumHeights.Length; i++) 
     { 
      finalHeights[i] = minimumHeights[i]; 
      availableHeight -= finalHeights[i]; 
     }    

     //allow fixed-height rows their height - if some ignoramus made fixed-heights too big, we can't help him 
     for (int i = 0; i < desiredHeights.Length; i++) 
     { 
      if (this.RowDefinitions[i].Height.IsAbsolute) 
      { 
       finalHeights[i] = this.RowDefinitions[i].Height.Value; 
       availableHeight = availableHeight + minimumHeights[i] - finalHeights[i]; 
      } 
     } 

     //allow auto-size rows their desired heights, so long as there's height left to be had 
     for (int i = 0; i < desiredHeights.Length; i++) 
     {     
      if (this.RowDefinitions[i].Height.IsAuto) 
      { 
       double desiredHeightIncrease = desiredHeights[i] - minimumHeights[i]; 

       if (desiredHeightIncrease <= availableHeight) 
       { 
        finalHeights[i] += desiredHeightIncrease; 
        availableHeight -= desiredHeightIncrease; 
       } 
       else 
       { 
        finalHeights[i] = minimumHeights[i] + availableHeight; 
        availableHeight = 0; 
       } 
      } 
     } 

     //now that auto-size rows have been prevented from getting out of control, make the min heights of any star-size rows available again 
     for (int i = 0; i < desiredHeights.Length; i++) 
     { 
      if (this.RowDefinitions[i].Height.IsStar) 
      { 
       availableHeight += minimumHeights[i]; 
      } 
     } 

     //divide any leftover available height proportionally amongst the star-sized rows, while there's height left to be had 
     double totalStarValues = 0; 
     for (int i = 0; i < desiredHeights.Length; i++) 
     { 
      if (this.RowDefinitions[i].Height.IsStar) 
      { 
       totalStarValues += this.RowDefinitions[i].Height.Value; 
      } 
     } 

     for (int i = 0; i < desiredHeights.Length; i++) 
     { 
      if (this.RowDefinitions[i].Height.IsStar) 
      { 
       finalHeights[i] = availableHeight * (this.RowDefinitions[i].Height.Value/totalStarValues); 
      } 
     } 

     //decide the vertical position of each row 
     double[] rowPositions = new double[desiredHeights.Length]; 
     rowPositions[0] = 0; 
     for (int i = 1; i < rowPositions.Length; i++) 
     { 
      rowPositions[i] = rowPositions[i - 1] + finalHeights[i - 1]; 
     } 

     //tell children to lay themselves out based on these results 
     foreach (UIElement child in this.InternalChildren) 
     { 
      int row = Grid.GetRow(child); 

      //special case for scrollviewer, which doesn't size itself appropriately 
      if (child is ScrollViewer) 
      { 
       ScrollViewer scrollViewer = child as ScrollViewer; 

       //temporarily update its height value, JUST for the Arrange() call 
       double oldHeight = scrollViewer.Height; 
       scrollViewer.Height = finalHeights[row]; 
       child.Arrange(new Rect(0, rowPositions[row], arrangeSize.Width, finalHeights[row])); 

       //restore the original value 
       scrollViewer.Height = oldHeight; 
      } 

      //typical case for non-scroll-viewers 
      else 
      { 
       child.Arrange(new Rect(0, rowPositions[row], arrangeSize.Width, finalHeights[row])); 
      } 
     } 

     return arrangeSize; 
    } 
} 

ここにテストケースがあります。これをウィンドウにドロップし、ウィンドウのサイズを変更して機能することを確認します。

<local:NoStretchGrid VerticalAlignment="Stretch"> 

    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="2*"/> 
     <RowDefinition Height="*"/> 
     <RowDefinition Height="50"/> 
    </Grid.RowDefinitions> 

    <ScrollViewer VerticalScrollBarVisibility="Visible" MinHeight="50"> 
     <Rectangle Fill="Orange" Height="250"/> 
    </ScrollViewer> 

    <ScrollViewer VerticalScrollBarVisibility="Visible" Grid.Row="1" MinHeight="50"> 
     <Rectangle Fill="Blue" Height="200"/> 
    </ScrollViewer> 

    <Grid Background="Pink" Grid.Row="2" MinHeight="30"/> 
    <Grid Background="Green" Grid.Row="3" MinHeight="30"/> 
    <Grid Background="Red" Grid.Row="4"/> 

</local:NoStretchGrid>