3
votes

I've got a window containign a textblock with variable text and a stackpanel which contains a variable amount of buttons. My Window How can I let the window grow in width (horizontally), when the stackpanel grows in width but let the text always adapt to the available width (not grow the window in width when the text gets longer).

I've tried some combinations with and without Width="Auto" / VerticalAlign="Stretch" / TextWrapping etc. but I only get the window to grow if either one (text length / stackpanel children) grow in width.

I want the text to wrap based on the width of the window which shall be based on the width of the stackpanel. XAML only would be nice.

Edit

As requested some stripped-down markup to reproduce the issue:

<Window 
    Width="500" 
    Height="225" 
    MinWidth="500" 
    MinHeight="225"
    HorizontalAlignment="Stretch"
    SizeToContent="WidthAndHeight"
    ResizeMode="NoResize">

    <Grid HorizontalAlignment="Stretch" Width="Auto">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="35"/>
        </Grid.RowDefinitions>

        <!-- Text should adapt to the available width but not grow the window. Instead it should scroll vertically -->
        <ScrollViewer Grid.Row="0" Margin="15" VerticalScrollBarVisibility="Auto">
            <TextBlock TextWrapping="Wrap" Height="Auto">...Some Binding...</TextBlock>
        </ScrollViewer>

        <!-- Should be as wide as it needs to be, growing the window in width if neccessary -->
        <StackPanel x:Name="ButtonStackPanel" Grid.Row="1" FlowDirection="RightToLeft" Orientation="Horizontal" Width="Auto">
            ...Some Buttons...
        </StackPanel>
    </Grid>
</Window>

Edit 2 The window is supposed to have a MinWidth of 500, if there's only one button in the stackpanel, it should still stretch full width (500px).

Edit 3 - Some more pictures

Correct - Text is short, only one button -> MinWidth 500 enter image description here Wrong - Text is long and should wrap at the MinWidth of 500 because only the Stackpanel should be allowed to grow the window in width enter image description here Correct - Short text but stack panel grows the window enter image description here

2
Please post some XAML markup that can be used to reproduce your issue. How wide is the TextBlock supposed to be? - mm8
@mm8 as I wrote in the second and fourth paragraph, as wide as it can be. But it should not grow the window. Only the stackpanel should grow the window's width. - Hexo
@mm8 markup added - Hexo
You can bind MaximumWidth of one control to ActualWidth of another. - Sinatr

2 Answers

3
votes

You have to bind the Width of the Window to the outer Grid's Width (as Mighty Badaboom suggested) and the Width of the ScrollViewer to the ActualWidth of your StackPanel.

Here is how you can achieve it:

<Window SizeChanged="MainWindow_OnSizeChanged" Height="225" SizeToContent="Width" ResizeMode="NoResize">
<Grid Name="OuterGrid" MinWidth="500">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="35"/>
    </Grid.RowDefinitions>


    <ScrollViewer Grid.Row="0" Margin="15" VerticalScrollBarVisibility="Auto">
        <TextBlock TextWrapping="Wrap" Height="Auto">...Some Binding...</TextBlock>
    </ScrollViewer>

    <StackPanel Grid.Row="1" HorizontalAlignment="Stretch" Background="Blue">
        <StackPanel x:Name="ButtonStackPanel" HorizontalAlignment="Center" Grid.Row="1" FlowDirection="RightToLeft" Orientation="Horizontal" Width="Auto">
            ...Some Buttons...
        </StackPanel>
    </StackPanel>
</Grid>
</Window>

And code behind:

private void MainWindow_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
    if (e.WidthChanged)
        OuterGrid.Width = Panel.ActualWidth;
}


EDIT: Added an outer StackPanel to support Stretching & blue Background requirement.

EDIT 2: It seems that you can't achieve the behavior you're describing with only XAML. Although the fix is a few lines of code away.

2
votes

You could write your own custom control and override measure/arrange in a way that width and height is dominated by different child elements. Note I'm cheating a bit here by just using the first two children of a panel and giving them a different meaning based on your question, without even checking for valid child indices first etc.:

public class SizeAwareControl : Panel
{
    private UIElement HeightDeterminingContent
    {
        get { return InternalChildren[0]; }
    }
    private UIElement WidthDeterminingContent
    {
        get { return InternalChildren[1]; }
    }

    protected override Size MeasureOverride(Size constraint)
    {
        Size result = new Size(double.PositiveInfinity, double.PositiveInfinity);

        WidthDeterminingContent.Measure(result);

        result.Width = WidthDeterminingContent.DesiredSize.Width;

        HeightDeterminingContent.Measure(result);

        result.Height = WidthDeterminingContent.DesiredSize.Height + HeightDeterminingContent.DesiredSize.Height;

        return result;
    }
    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        HeightDeterminingContent.Arrange(new Rect(0, 0, WidthDeterminingContent.DesiredSize.Width, HeightDeterminingContent.DesiredSize.Height));
        WidthDeterminingContent.Arrange(new Rect(0, HeightDeterminingContent.DesiredSize.Height, WidthDeterminingContent.DesiredSize.Width, WidthDeterminingContent.DesiredSize.Height));
        return new Size(WidthDeterminingContent.DesiredSize.Width, WidthDeterminingContent.DesiredSize.Height + HeightDeterminingContent.DesiredSize.Height);
    }
}

Usage

<local:SizeAwareControl>
    <!-- First child determines the height -->
    <ScrollViewer Margin="15" VerticalScrollBarVisibility="Auto" MaxHeight="600">
        <TextBlock TextWrapping="Wrap" Height="Auto">...Some Binding...</TextBlock>
    </ScrollViewer>
    <!-- Second child determines the width -->
    <StackPanel x:Name="ButtonStackPanel" Orientation="Horizontal" MinWidth="500">
        <Button Width="100" Content="..." Click="MenuItem_Click"/>
        <Button Width="100" Content="..."/>
        <Button Width="100" Content="..."/>
    </StackPanel>
</local:SizeAwareControl>

This is just a starting point - you may need some more detailed logic to determine good width and height values. The MaxHeight on the first child enables scrolling for long text (instead of growing a very tall window) and the MinWidth on the second child ensures width for a low amount of buttons.