简体   繁体   中英

WPF Grid and DataGrid sizing

The UserControl I'm trying to work with is essentially laid out like so:

<Grid>
   <Grid.RowDefinitions>
       <Row Height="*"/>
       <Row Height="*"/>
   </Grid.RowDefinitions>
   <wpftoolkit:DataGrid x:Name="PrimaryGrid"/> <!-- ~10 rows -->
   <Border>
      <Grid>
         <Grid.RowDefinitions>
             <Row Height="*"/>
             <Row Height="*"/>
         </Grid.RowDefinitions>
         <wpftoolkit:DataGrid x:Name="SecondaryGrid"/> <!-- ~2 rows -->
         <wpftoolkit:DataGrid x:Name="OtherGrid"/> <!-- ~50 rows -->
      </Grid>
   </Border>
</Grid>

This UserControl is then placed in my Window as the last member of a Grid , the only one with Height="*" . I'm having problems with the way the DataGrid s are sized.

If I set VerticalAlignment of the UserControl to Stretch in the Window , then PrimaryGrid gets 1/2 height of the UserControl , and each of the two inside the Border get 1/4. They are sized like this regardless of the number of rows each have, leaving OtherGrid with too little vertical space and the others with non-row whitespace inside the scrollview.

If I set VerticalAlignment to Top , the grids seem to size pretty well to their contents, except there is an inexplicable whitespace being left at the bottom of the UserControl . I used Snoop and the RowDefinition for the UserControl has the proper ActualHeight , but the UserControl only uses a portion of it - 80% or so.

I don't really mind whether I fix the Stretch case (How do I make the DataGrid not stretch larger than its number of rows?) or the Top case (How do I make the UserControl use all the space it has available to it?)

: Use Stretch for the UserControl, but Auto (instead of * ) for the row heights inside your UserControl. :对UserControl使用Stretch ,但对UserControl中的行高使用Auto (而不是* )。

: "Auto" means : as much space as needed (which is what you want), whereas "*" means: a proportional share of all available space (resulting in the 1/2, 1/4, 1/4-distribution). :“自动” 表示 :尽可能多的空间(这是您想要的),而“*”表示:所有可用空间的比例份额(产生1 / 2,1 / 4,1 / 4分布) )。

Since you want the UserControl to use all available space, Stretch is the correct option (it means exactly that). Set one of the row heights inside the UserControl back to "*", if you want this row to take up the remaining available space.

This is a common problem where what you really want is 2 completely different layout behaviors: Auto sizing when there's room for all three, * sizing when there isn't. Some quick fixes you can try out with limitations:

  • Auto sizing (as already mentioned)
  • DockPanel with each set to Dock=Top - this will have a similar effect to VerticalAlignment=Top but the last DataGrid (only 1) will stretch out to fill the remaining space. Also bad if the first or second take up more space than exists because they'll push the others out.
  • Set MinHeight/MaxHeight in combination with one of the other 2 changes on your DataGrids to keep them from getting out of control. This gives up some of the auto-layout flexibility in exchange for making sure everything shows up.

Beyond those you can try something more complex like creating a custom Panel (or find one that someone else made already), or creating a MultiValueConverter that can calculate appropriate Height (or MinHeight, MaxHeight) settings for each DG or Row based on the height of the UC and each of the DGs.

Well, here's what I ended up with. It does what I want from a layout point of view, mostly. A bit more code behind than I'd like, but oh well.

In my DataContextChanged event handler:

//for each grid
_reportObserver = new PropertyObserver<ItemCollection>(PrimaryGrid.Items)
    .RegisterHandler(c => c.Count, c => UpdateMaxHeight(PrimaryGrid));
UpdateMaxHeight(PrimaryGrid);

PropertyObserver is from http://joshsmithonwpf.wordpress.com/2009/07/11/one-way-to-avoid-messy-propertychanged-event-handling/

//Lots of ugly hard-coding     
private void UpdateMaxHeight(DataGrid grid)
{
   double header_height = grid.ColumnHeaderHeight;
   if (double.IsNaN(header_height))
      header_height = 22;
   double margin_height = grid.Margin.Bottom + grid.Margin.Top;
   grid.MaxHeight = header_height + margin_height + grid.Items.Count * (grid.RowHeight+2);

   UpdateLayout(); //this is key for changes to number of items at runtime
}

Even after setting the DataGrid's MaxHeight, things were still ugly, so I had to set the max height on the RowDefinition's too. But that still wasn't right, causing the margin_height addition above.

<RowDefinition Height="*" MaxHeight="{Binding ElementName=PrimaryGrid, Path=MaxHeight}"/>

At some point, I'll take into account my optionally visible row details in my ugly max height code.

As far as Top vs Stretch, I ended up for other reasons having the usercontrol in a ListView. Everything sizes nicely now.

Thanks again for looking at my problem.

Just wanted to follow up on this problem. Over time, the solution I provided above just did not meet users expectations. I've now changed to a scheme like this:

<ScrollViewer VerticalScrollBarVisibility="Auto">
   <Grid>
      <!-- row definitions -->
      <KentBoogart's Resizer ResizeDirection="South">
         <DataGrid/>
      </kb:Resizer>
      <kb:Resizer ResizeDirection="South">
         <DataGrid/>
      </kb:Resizer>
   </Grid>
</ScrollViewer>

In another place where I've used this idiom, I've set the Resizer to have a MaxHeight bound to the ScrollViewer's ActualHeight to keep it from going out of control. This design can be a little confusing with the overall scrollbar, plus scrollbars in the DataGrid, but with good borders and margins, it's not too bad.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM