Reputation: 181
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
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:
DesiredSize
property does indeed change, then this change will be noticed by the element's layout parent, giving the second condition... 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.
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
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>
Here, you can see that the different StrokeThickness
values make absolutely no difference to the size or positions of the Rectangle
s.
Upvotes: 1
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
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