pat3d3r
pat3d3r

Reputation: 181

WPF Does rectangle stroke thickness affect location?

I need a quick advice on drawing rectangles for printing. If I define

new drawingContext.DrawRectangle(new SolidColorBrush(Colors.Brown), 
                                 new Pen(new SolidColorBrush(Colors.Black), 3), 
                                 new Rect(new Point (1, 1), new Size(Width, Height))) 

lies the left upper outermost corner of the rectangle on (1, 1) or is there an offset because of the stroke thickness?

I already tried some variations to figure it out, but always get different results and can not find a pattern there.

Edit:

protected override void OnRender(DrawingContext drawingContext)
    {
        Point start = new Point(1.5, 1.5);
        drawingContext.DrawRectangle(new SolidColorBrush(Colors.Brown), new Pen(new SolidColorBrush(Colors.Black), 3), new Rect(start, new Size(Width-2.5, Height-2.5)));

        base.OnRender(drawingContext);
    }

I tried that, assuming the reference point of the rectangle is located in the middle of the stroke with 3 thickness. That seems to be right, at least at left and upper side of the rectangle. I also do have a margin defined, with Thickness 1.

Width and Height correspond to printableWidth and printableHeight. Therefore I go from (1.5, 1.5) to (Width-2.5, Height-2.5). 1 for the margin and 1.5 to be in the middle of the stroke. But looking at the print, the rectangle does not fit at right and bottom sides.

Upvotes: 0

Views: 3215

Answers (4)

Glenn Slayden
Glenn Slayden

Reputation: 18829

Expanding on the answers here, don't confuse StrokeThickness with BorderThickness. The FrameworkElement inheritance hierarchy has different branches

  • Shape which includes the UI-ready Rectangle class discussed here versus...
  • Decorator framing elements with a Child property where the frames have thickness, e.g. Border ...
  • Control, usually with some concept of Content, and some of which may also mimic or expose their own BorderThickness/BorderBrush properties from Decorator

For the purposes of the current question, modifying the StrokeThickness or BorderThickness on any FrameworkElement can only influence its location if:

  1. The element itself first decides that the change affects its own overall size. If the change doesn't affect its own actual size, then there's no way its location can move. But if the element's DesiredSize property does indeed change, then this change will be noticed by the element's layout parent, giving the second condition...
  2. ...and also, if as a result of a DesiredSize change, the layout parent decides to move the element.

So the correct answer to the question as posed is "possibly," because it depends on both the particular element in question (Rectangle in the OP's case) as well as the layout parent to which it is attached.

For the OP's particular example, explicitly setting the Width and Height properties on Rectangle is interpreted as requesting the final overall size, which will include the full outer extent of its Stroke. This mode has the effect of always reducing the inner area, because the Stroke will always be taken from, or "forced into" the inside of the overall dimensions, because it's more important for the final DesiredSize to agree with Width/Height.

Furthermore, I believe Rectangle will never "grow" its size for the purpose of satisfying an overly-fat StrokeThickness that fills beyond the entire interior of the shape. So, since condition (1.) can never be satisfied for Rectangle, the other condition is moot, and it is therefore the case that changing the StrokeThickness on a Rectangle can never cause it to move.

I modified the code @Sheridan provided to compare the use of the Shape-derived element Rectangle--which doesn't contain any layout children (left column)--with Decorator-derived element Border, containing a TextBlock as a child element that reports the interior available size (right column). You can see that as the StrokeThickness changes, the ActualWidth/ActualHeight of the Rectangle itself do not change, but that's not the case for the TextBlock contained inside the Border element.

enter image description here

Here's the experimentation code, in case anyone wants to play with it further:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>

    <!-- Insert Grid.Resources (shown separately below) here -->

    <Rectangle x:Name="r1" Grid.Column="0" Grid.Row="0" StrokeThickness="1" />
    <Label Grid.Column="0" Grid.Row="0" Target="{x:Reference r1}" />
    <Rectangle x:Name="r2" Grid.Column="0" Grid.Row="1" StrokeThickness="5" />
    <Label Grid.Column="0" Grid.Row="1" Target="{x:Reference r2}" />
    <Rectangle x:Name="r3" Grid.Column="0" Grid.Row="2" StrokeThickness="10" />
    <Label Grid.Column="0" Grid.Row="2" Target="{x:Reference r3}" />
    <Rectangle x:Name="r4" Grid.Column="0" Grid.Row="3" StrokeThickness="15" />
    <Label Grid.Column="0" Grid.Row="3" Target="{x:Reference r4}" />

    <Border Grid.Column="1" Grid.Row="0" BorderThickness="1" Child="{StaticResource dt}" />
    <Border Grid.Column="1" Grid.Row="1" BorderThickness="5" Child="{StaticResource dt}" />
    <Border Grid.Column="1" Grid.Row="2" BorderThickness="10" Child="{StaticResource dt}" />
    <Border Grid.Column="1" Grid.Row="3" BorderThickness="15" Child="{StaticResource dt}" />

</Grid>


<Grid.Resources>
    <TextBlock x:Key="dt"
               x:Shared="False" 
               Foreground="White" 
               DataContext="{Binding RelativeSource={RelativeSource Self}}">
        <Run Text="{Binding Path=DataContext.ActualWidth,StringFormat='ActualWidth: {0}',Mode=OneWay}" />
        <LineBreak />
        <Run Text="{Binding Path=DataContext.ActualHeight,StringFormat='ActualHeight: {0}',Mode=OneWay}" />
    </TextBlock>

    <Style TargetType="Label">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Label">
                    <TextBlock Foreground="White"
                            DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                            Width="{Binding Path=Target.ActualWidth}"
                            Height="{Binding Path=Target.ActualHeight}">
                        <Run Text="{Binding Path=Target.ActualWidth,StringFormat='ActualWidth: {0}',Mode=OneWay}" />
                        <LineBreak />
                        <Run Text="{Binding Path=Target.ActualHeight,StringFormat='ActualHeight: {0}',Mode=OneWay}" />
                    </TextBlock>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="common_style" TargetType="FrameworkElement">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Width" Value="150" />
        <Setter Property="Height" Value="80" />
    </Style>

    <Style TargetType="Border" BasedOn="{StaticResource common_style}">
        <Setter Property="BorderBrush" Value="Navy" />
        <Setter Property="Background" Value="Gray" />
    </Style>

    <Style TargetType="Rectangle" BasedOn="{StaticResource common_style}">
        <Setter Property="Stroke" Value="Navy" />
        <Setter Property="Fill" Value="Gray" />
    </Style>
</Grid.Resources>

Upvotes: 0

Sheridan
Sheridan

Reputation: 69989

It would be pretty easy to simply test your hypothesis... perhaps even quicker than asking a question here:

<Grid Height="230">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Rectangle Grid.RowSpan="4" Grid.Column="0" Fill="Black" Stroke="Black" StrokeThickness="1" Width="1" Height="230" Margin="5,0" HorizontalAlignment="Right" />
    <Rectangle Grid.Row="0" Grid.Column="1" Fill="White" Stroke="Black" StrokeThickness="1" Width="200" Height="50" />
    <Rectangle Grid.Row="1" Grid.Column="1" Fill="White" Stroke="Black" StrokeThickness="5" Width="200" Height="50" />
    <Rectangle Grid.Row="2" Grid.Column="1" Fill="White" Stroke="Black" StrokeThickness="10" Width="200" Height="50" />
    <Rectangle Grid.Row="3" Grid.Column="1" Fill="White" Stroke="Black" StrokeThickness="15" Width="200" Height="50" />
    <Rectangle Grid.RowSpan="4" Grid.Column="2" Fill="Black" Stroke="Black" StrokeThickness="1" Width="1" Height="230" Margin="5,0" HorizontalAlignment="Left" />
</Grid>

enter image description here

Here, you can see that the different StrokeThickness values make absolutely no difference to the size or positions of the Rectangles.

Upvotes: 1

Dan Puzey
Dan Puzey

Reputation: 34218

To test this yourself, draw two rectangles at the same position, with different coloured borders.

new drawingContext.DrawRectangle(new SolidColorBrush(Colors.Brown), 
                             new Pen(new SolidColorBrush(Colors.Red), 6),
                             new Rect(new Point (1, 1), new Size(Width, Height)));
// Note: second rectangle should have a *smaller* stroke thickness
new drawingContext.DrawRectangle(new SolidColorBrush(Colors.Brown), 
                             new Pen(new SolidColorBrush(Colors.Black), 3), 
                             new Rect(new Point (1, 1), new Size(Width, Height)));

If the brush width affects the position of the rectangle, you'll see the red border around the edge of the second rectangle. No border, no effect.

Upvotes: 0

Jammer
Jammer

Reputation: 10206

As far as I know the total space occupied by a control is bound by its hosting UIElement and cannot logically extend beyond these boundaries unless you specifically set it up to do so. The ClipToBounds setting will visually truncate anything extending beyond these bounds.

Or you are working with Adorners.

Upvotes: 0

Related Questions