Search code examples
wpflayouttextblocksizing

WPF - Complex TextBlock layout


For the past few days I've been trying to wrap my head around the following layout of TextBlocks:

-------------------- -----------
|         1        | |         |
-------------------- |    3    |
|       2       |    |         |
-----------------    -----------

All of the TextBlocks have dynamic width. They have fixed height and are in a fixed-size container. They need to meet the following requirements:

  • 2 has maximum priority - it needs to always be at full length.
  • 3 can fill the remaining space left after sizing 2.

When ignoring TextBlock 1, these two requirements could be fulfilled by putting the two other TextBlocks in a Grid, with columns set to "auto" and "*" respectively.

There is a third requirement though:

  • 1 assumes all the space it can, but without restricting 3.

Examples:

Long content in block 3 (block 2 content is full length, block 3 content gets trimmed):

----------------- -------------
|       1       | |           |
----------------- |     3     |
|       2       | |           |
----------------- -------------

Short content in block 3 (both block 2 and 3 content are full length; block 1 fills the remaining space):

--------------------- ---------
|         1         | |       |
--------------------- |   3   |
|       2       |     |       |
-----------------     ---------

Is there a way to achieve this layout in WPF? How?


Solution

  • I don't think this is possible with pure XAML. One way to accomplish your requirements is implementing your own panel that does all the magic. Here is one implementation:

    public class SpecialPanel : Panel
    {
        protected override Size MeasureOverride (Size constraint)
        {
            foreach (UIElement child in Children)
            {
                child.Measure (constraint);
            }
    
            return constraint;
        }
    
        protected override Size ArrangeOverride (Size arrangeSize)
        {
            if (VisualChildrenCount != 3)
            {
                return (arrangeSize);
            }
    
            Size sizeLabel0 = InternalChildren[0].DesiredSize;
            Size sizeLabel1 = InternalChildren[1].DesiredSize;
            Size sizeLabel2 = InternalChildren[2].DesiredSize;
    
            InternalChildren[1].Arrange (new Rect (0, sizeLabel0.Height, sizeLabel1.Width, sizeLabel1.Height));
            var maxRemainingWidthFor2 = arrangeSize.Width - sizeLabel1.Width;
    
            if (maxRemainingWidthFor2 <= 0)
            {
                InternalChildren[0].Arrange (new Rect (0, 0, sizeLabel0.Width, sizeLabel0.Height));
            }
            else
            {
                if (maxRemainingWidthFor2 < sizeLabel2.Width)
                {
                    InternalChildren[2].Arrange (new Rect (arrangeSize.Width - maxRemainingWidthFor2, 0, maxRemainingWidthFor2, sizeLabel2.Height));
                    InternalChildren[0].Arrange (new Rect (0, 0, Math.Min (sizeLabel0.Width, sizeLabel1.Width), sizeLabel0.Height));
                }
                else
                {
                    var max0 = arrangeSize.Width - maxRemainingWidthFor2;
                    var width0 = Math.Min (sizeLabel0.Width, max0);
    
                    InternalChildren[2].Arrange (new Rect (arrangeSize.Width - sizeLabel2.Width, 0, sizeLabel2.Width, sizeLabel2.Height));
                    InternalChildren[0].Arrange (new Rect (0, 0, arrangeSize.Width - sizeLabel2.Width, sizeLabel0.Height));. 
                }
            }
    
            return arrangeSize;
        }
    }
    

    Usage:

        <local:SpecialPanel>
            <Label
                x:Name="Label1"
                VerticalContentAlignment="Center"
                Content="TextBlock00000000000000" />
            <Label
                x:Name="Label2"
                VerticalContentAlignment="Center"
                Content="TextBloc111111111111" />
            <Label
                x:Name="Label3"
                HorizontalContentAlignment="Center"
                VerticalContentAlignment="Center"
                Content="Label333333" />
        </local:SpecialPanel>