Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Horizontal dashed line stretched to container width

Tags:

layout

wpf

line

I have a layout contained within a ScrollViewer in which I need to draw a horizontal dashed line that stretches to the full width of the container. The closest I've managed is the following

<ScrollViewer HorizontalScrollBarVisibility="Auto">
    <StackPanel>
        <Button Width="400" Height="50" VerticalAlignment="Top" Margin="10" />
        <Line HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Stroke="Black"
              X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"
              StrokeDashArray="2 2" StrokeThickness="1" />
    </StackPanel>
</ScrollViewer>

Sample looking good

This nearly works, however once the container (in my case a window) has been enlarged, the line doesn't shrink back down to the appropriate size when the container is sized back down. The below is the screenshot of the same window after I have horizontally sized the window up and down.

Screenshot of the sample after increasing and reducing the size of the window

Note that the fact that the line is dashed is important as it means that solutions that involve stretching the line don't work (the dashes appear stretched).

I know that this is because of the X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" binding (by design the line is always the widest thing in the scrollable region, so when I size the window down the scrollable region the line defines the width of the scrollable region), however I can't think of a solution.

How can I fix this problem?


Screenshot of why using ViewportWidth doesn't work

Screenshot of why using ViewportWidth doesn't work

like image 722
Justin Avatar asked May 15 '13 09:05

Justin


2 Answers

I realised that what I needed was for the Line to ask for zero space during the measure step of layout, but then use up all the available space during the arrange step. I happened to stumble across the question Make WPF/SL grid ignore a child element when determining size which introduced the approach of using a custom decorator which included this logic.

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

(I was hoping that some existing layout control incorporated this logic to avoid having to write my own layout logic, however the logic here is so simple that I'm not really that fussed). The modified XAML then becomes

<ScrollViewer HorizontalScrollBarVisibility="Auto">
    <StackPanel>
        <Button Width="400" Height="50" VerticalAlignment="Top" Margin="10" />
        <local:NoSizeDecorator Height="1">
            <Line Stroke="Black" HorizontalAlignment="Stretch"
                  X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"
                  StrokeDashArray="2 2" StrokeThickness="1" />
        </local:NoSizeDecorator>
    </StackPanel>
</ScrollViewer>

This works perfectly

like image 58
Justin Avatar answered Nov 04 '22 05:11

Justin


You may put a very long Line in a left-aligned Canvas with zero Width and ClipToBounds set to false.

<ScrollViewer HorizontalScrollBarVisibility="Auto">
    <StackPanel>
        <Button Width="400" Height="50" VerticalAlignment="Top" Margin="10" />
        <Canvas HorizontalAlignment="Left" Width="0" ClipToBounds="False">
            <Line Stroke="Black" StrokeDashArray="2 2" X2="10000"/>
        </Canvas>
    </StackPanel>
</ScrollViewer>
like image 24
Clemens Avatar answered Nov 04 '22 07:11

Clemens