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
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);
}
}
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>