I'm using a Viewbox to scale a fixed size canvas (containing various paths that together form a meaningful picture).
<StackPanel Background="Red" Width="400" UseLayoutRounding="True">
<StackPanel.Resources>
<Style TargetType="Viewbox">
<Setter Property="Height" Value="400" />
<Setter Property="Margin" Value="0,0,0,50" />
</Style>
<Style TargetType="Path">
<Setter Property="Stroke" Value="Blue" />
<Setter Property="StrokeThickness" Value="2" />
</Style>
</StackPanel.Resources>
<Viewbox>
<Canvas Width="5" Height="5">
<Path Data="M 1,1 h 3 v 3 h -3 z" />
</Canvas>
</Viewbox>
<Viewbox>
<Canvas Width="6" Height="6">
<Path Data="M 1,1 h 4 v 4 h -4 z" />
</Canvas>
</Viewbox>
</StackPanel>
This xaml appears as shown on top when rendered natively on my PC and as shown on the bottom when rendered on an emulator. As the emulator is producing the desired result can someone please explain to me why the second path extends beyond the red panel?
That's because Microsoft's new application platform is designed to be more limited. Apps are run in a sandbox, limiting the files they can access on your system.
There really isn't a discrepancy... what's happening is that you are taking something that renders really small (your path is very small):
look at the tiny boxes
...and scaling it up extremely large and telling the layout to round to the nearest whole pixel via UseLayoutRounding
the reason why the result is unreliable is that the Path
s are being scaled independently of the Canvas
since it doesn't actually layout its contents. So when one of your path points land on an uneven pixel boundary (which could happen at some screen resolutions and not others) it's rounding to the nearest virtual pixel inside the un-scaled the canvas and this is getting scaled from the Viewbox
and then rounded again to the nearest whole pixel (using the same scale the Viewbox
determined by the scaling of the Canvas
) exacerbating the rounding error. You can see this illustrated more easily if we turn off the StackPanel.Background
(default is transparent) set the Canvas.Background
to green and dial down the opacity on the color of the stroke:
you can see the path rounding is different than the canvas
So you either have to turn off UseLayoutRounding
so that there is decimal points carried through the operation or simplify your layout so that the errors don't occur.
For instance, if you let the paths scale themselves by removing the inappropriate and superfluous fixed size canvas and setting Path.Stretch = Uniform
you end up with:
<StackPanel Background="Red" Width="400" UseLayoutRounding="True">
<StackPanel.Resources>
<Style TargetType="Viewbox">
<Setter Property="Height" Value="400" />
<Setter Property="Margin" Value="0,0,0,50" />
</Style>
<Style TargetType="Path">
<Setter Property="Stroke" Value="Blue" />
<Setter Property="StrokeThickness" Value="2" />
<Setter Property="Stretch" Value="Uniform" />
</Style>
</StackPanel.Resources>
<Viewbox>
<Path Data="M 1,1 h 3 v 3 h -3 z" />
</Viewbox>
<Viewbox>
<Path Data="M 1,1 h 4 v 4 h -4 z" />
</Viewbox>
</StackPanel>
resulting in:
probably what you were actually looking for
However if this was an over simplification and you actually have multiple paths that you intended to put inside your Canvas
, I suggest you use a container that actually does its own layout, the obvious choice is a Grid
this way you can still have the paths scale themselves along with the Grid
keeping them in lock-step (Canvas
s won't layout their children). The code would look like:
<StackPanel Background="Red" Width="400" UseLayoutRounding="True">
<StackPanel.Resources>
<Style TargetType="Viewbox">
<Setter Property="Height" Value="400" />
<Setter Property="Margin" Value="0,0,0,50" />
</Style>
<Style TargetType="Path">
<Setter Property="Stroke" Value="Blue" />
<Setter Property="StrokeThickness" Value="2" />
<Setter Property="Stretch" Value="Uniform" />
</Style>
</StackPanel.Resources>
<Viewbox>
<Grid Width="5" Height="5">
<Path Data="M 1,1 h 3 v 3 h -3 z" />
</Grid>
</Viewbox>
<Viewbox>
<Grid Width="6" Height="6">
<Path Data="M 1,1 h 4 v 4 h -4 z" />
</Grid>
</Viewbox>
</StackPanel>
and the result is identical to the one without a secondary container
hope this helps -ck
I could reproduce the problem on my machine with emulator at display resolution of 2560x1440 with canvas as below.
<Canvas Width="4" Height="4">
<Rectangle Width="4" Height="4" Fill="Blue" ></Rectangle>
</Canvas>
For above snippet - canvas overflows/goes-beyond the red panel.
<Canvas Width="6" Height="6">
<Rectangle Width="6" Height="6" Fill="Blue" ></Rectangle>
</Canvas>
For above snippet - canvas underflows/does-not-fit the red panel, and leaves some space on right margin of the red panel.
if the canvas size is kept multiple of 5, it fits properly in red panel.
It does not seem to be an issue with Path since it reproduces with rect also. It seem to be issue with ViewBox scaling. I found that with UseLayoutRounding="False"
on stackpanel - issues does not reproduce. I also found a related thread when looking around for ViewBox scaling issues. It suggest to use SnapsToDevicePixels
property but that is not available for windows store xaml apps.
In summary, it seem to be an issues with ViewBox scaling though I am not a pro with internal knowledge of viewbox scaling algo. Possible workaround is to set UseLayoutRounding to false if it passes the test on another machine. The multiple of 5 does not look like as good workaround but you can check. On a separate note - if you were just curious to know why it is happening, you get the answer and may be underflow/overflow of few pixels may out matter for your application.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With