I have a Grid which has several children, one of which is a ScrollViewer. I want the Grid to size itself based on all of its children except the ScrollViewer, which I want to just take up the space that is created by sizing the Grid for the rest of its children. (For example, the Grid is 2x2 and the ScrollViewer is in Row 0, Column 0, and so the other three Grid entries are enough to determine the dimensions of the Grid.)
Is there a nice way to do this? I've looked into creating a Custom Panel either to replace the Grid or to contain the ScrollViewer, but at no point during the MeasureOverride/ArrangeOverride calls do I ever get a call which can tell me about the final width/height of the grid row/column I care about (eg, Row 0, Column 0).
One thought I had was to derive from Grid and call the base MeasureOverride/ArrangeOverride but remove the ScrollViewer from the Children of the Grid right before the call (and p开发者_运维技巧ut it back afterwards), but messing with the visual tree during layout computation seems like a bad idea.
Here's an example:
<Grid x:Name="LayoutRoot" Background="White">
<Grid VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
<Button Height="300" Width="300"/>
</ScrollViewer>
<Button Grid.Row="1" Grid.Column="0" Height="100" Width="100" Content="1,0"/>
<Button Grid.Row="0" Grid.Column="1" Height="100" Width="100" Content="0,1"/>
<Button Grid.Row="1" Grid.Column="1" Height="100" Width="100" Content="1,1"/>
</Grid>
</Grid>
I would like the size of the Grid to not change as the size of the Button in the ScrollViewer changes - e.g., I want the Grid to give the ScrollViewer the 100x100 square that is determined by the rest of the Grid's contents. And if I changed each of the 3 100x100 buttons to be 200x200, I would want the ScrollViewer to get 200x200, etc.
Pavlo's example gets me the closest so far, with the Grid rows/columns appropriately sized, but ScrollViewer does not adapt to the Size given to it when Arrange is called. See below:
If I understood correctly what you want, then you can do the following trick. Create a decorator that will ask for 0 space during the Measure stage and will arrange the child with all the given space at the Arrange stage:
public class NoSizeDecorator : Decorator
{
protected override Size MeasureOverride(Size constraint) {
// Ask for no space
Child.Measure(new Size(0,0));
return new Size(0, 0);
}
}
And your XAML will look like this:
<Grid x:Name="LayoutRoot" Background="White">
<Grid VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<my:NoSizeDecorator Grid.Row="0" Grid.Column="0">
<ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
<Button Height="300" Width="300" Content="0,0"/>
</ScrollViewer>
</my:NoSizeDecorator>
<Button Grid.Row="1" Grid.Column="0" Height="100" Width="100" Content="1,0"/>
<Button Grid.Row="0" Grid.Column="1" Height="100" Width="100" Content="0,1"/>
<Button Grid.Row="1" Grid.Column="1" Height="100" Width="100" Content="1,1"/>
</Grid>
</Grid>
Pavlo Glazkov's answer works very well for me! I have used it for a control that does not want to specify explicit sizes ... I added some logic and properties to defeat one dimension:
public sealed class NoSizeDecorator
: Decorator
{
/// <summary>
/// Sets whether the width will be overridden.
/// </summary>
public static readonly DependencyProperty DisableWidthOverrideProperty
= DependencyProperty.Register(
nameof(NoSizeDecorator.DisableWidthOverride),
typeof(bool),
typeof(NoSizeDecorator),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// Sets whether the width will be overridden.
/// </summary>
public bool DisableWidthOverride
{
get => (bool)GetValue(NoSizeDecorator.DisableWidthOverrideProperty);
set => SetValue(NoSizeDecorator.DisableWidthOverrideProperty, value);
}
/// <summary>
/// Sets whether the height will be overridden.
/// </summary>
public static readonly DependencyProperty DisableHeightOverrideProperty
= DependencyProperty.Register(
nameof(NoSizeDecorator.DisableHeightOverride),
typeof(bool),
typeof(NoSizeDecorator),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// Sets whether the height will be overridden.
/// </summary>
public bool DisableHeightOverride
{
get => (bool)GetValue(NoSizeDecorator.DisableHeightOverrideProperty);
set => SetValue(NoSizeDecorator.DisableHeightOverrideProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
UIElement child = Child;
if (child == null)
return new Size();
constraint
= new Size(
DisableWidthOverride
? constraint.Width
: 0D,
DisableHeightOverride
? constraint.Height
: 0D);
child.Measure(constraint);
return new Size(
DisableWidthOverride
? child.DesiredSize.Width
: 0D,
DisableHeightOverride
? child.DesiredSize.Height
: 0D);
}
}
精彩评论