WPF Path sizing problem
Hey guys, I've been playing around with WPF's Path shape, but I'm a bit annoyed with some behaviour. Specifically, the path does not size itself as I would like. If you look at the image below, what I want is for the entire path to be within the white square (which represents the bounds of the Path control), but the arcs hang out a bit. I think this is because Path sizes itself according to the points used to draw the shape, and not according to the shape that is actually drawn.
My question is: does anyone know how to overcome this? I mean, aside from explicitly setting the dimensions of the path. Is there some option that I have overlooked in order to get the path to size itself according to the shape, and not according to the points used to make the shape? Thanks for any answers.
Here's two versions of (what should be) equivalent code:
1) First, using databindings (written out in a very verbose manner):
<UserControl x:Class="OrbitTrapWpf.LineSegmentTool"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OrbitTrapWpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="Root" Background="White">
<UserControl.Resources>
<local:ArcSizeConverter x:Key="ArcSizeConverter"/>
<local:ArcPointConverter x:Key="ArcPointConverter"/>
</UserControl.Resources>
<Path Name="path" Stroke="Black">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsClosed="True">
<PathFigure.StartPoint>
<Binding ElementName="Root" Path="point0"></Binding>
</PathFigure.StartPoint>
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment SweepDirection="Counterclockwise" >
<ArcSegment.Size>
<Binding ElementName="Root" Path="Radius" Converter="{StaticResource ArcSizeConverter}"/>
</ArcSegment.Size>
<ArcSegment.Point>
<Binding ElementName="Root" Path="point1" />
</ArcSegment.Point>
</ArcSegment>
<LineSegment>
<LineSegment.Point>
<Binding ElementName="Root" Path="point2" />
</LineSegment.Point>
</LineSegment>
<ArcSegment SweepDirection="Counterclockwise">
<ArcSegment.Size>
<Binding ElementName="Root" Path="Radius" Converter="{StaticResource 开发者_如何学编程ArcSizeConverter}"/>
</ArcSegment.Size>
<ArcSegment.Point>
<Binding ElementName="Root" Path="point3" />
</ArcSegment.Point>
</ArcSegment>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
2) And this one, using the mini-language:
<UserControl x:Class="OrbitTrapWpf.LineSegmentTool"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OrbitTrapWpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="Root" Background="White">
<UserControl.Resources>
<local:ArcSizeConverter x:Key="ArcSizeConverter"/>
<local:ArcPointConverter x:Key="ArcPointConverter"/>
</UserControl.Resources>
<Grid Name="grid">
<Path Name="path" Stroke="Black" Data="M 0.146446609406726,1.14644660940673 A 0.5,0.5 0 1 0 0.853553390593274,1.85355339059327 L 1.85355339059327,0.853553390593274 A 0.5,0.5 0 1 0 1.14644660940673,0.146446609406726 Z " />
I thought that the two should be roughly the same, but apparently the mini-language version sizes nearly correctly, while the original is much different.
Basically, what your path xaml says, is:
- Start at Point0, draw an arc to Point1.
- From Point1, draw a line to Point2.
- From Point2, draw an arc to Point 3.
- 'IsClosed' draws another line from Point3 to Point0.
What you've defined is exactly what is being produced - the only way you can change it is to change your positions - but the arc will still extend beyond Point0 on the X axis because that's what you have defined.
If you need your shape to fit entirely within some boundary, you can put a border around your shape, with margin of, say, 1/2 radius (I'm sure there is a formula for the exact protrusion) at the bottom and right.
Since the second screenshot looks different to the first, I would conclude that they are different shapes - which can only mean that the path data was translated incorrectly.
Okay, so I found the problem and solved it. I had set the IsLargeArc
flag in the mini-language version, while in the purely XAML version I had left this as False
. So I changed it to True
, and I magically got the results I expected.
This seems to be a bug to me, because in this case the large and small arcs are one and the same, since I am drawing a half-arc. If anyone knows a reason for this behaviour, it would be awesome to hear about it!
I ran across this post and thought I would post an answer in case anyone is looking for a easy way to resize paths or Icons. The easiest way I have found is by using a Viewbox for all of my Path displays. This is because a path will scale itself nicely inside of a Viewbox. I use a Canvas to hold each path, the size of this Canvas is very important if you want to be able to use "Nice" numbers.
Here is a example of how to do this:
First (Optional) Draw your shape in a Vector Program like Inkscape or CorelDraw! I used CorelDraw to create the .svg File. Note: When using a program to create a vector make your page size something like 100 X 100 Pixels this is what you are going to set your Canvas Size to. If you are writing the path by hand this is also a very handy approach just pick a size like 100 X 100 and all of your path measurements are < 100 use that as your scale in other words.
Next use a translator program like Vector to Xaml Converter and generate a path. Save this into a Resource Dictionary or put it in the file where you need it. Put the Path inside a Canvas like so:
<Canvas x:Key="someName" Width="100" Height="100">
<Path Fill="#FF000000" Stroke="#FF373435" StrokeThickness="1" Data="M92,8L92,8C103,18,103,35,92,45L45,92C35,103,18,103,8,92L8,92C-3,82,-3,65,8,55L55,8C65,-3,82,-3,92,8z"/>
</Canvas>
Again note the size of the Canvas, this should match the dimensions of your "Drawing Board".
Then to use this just put a ContentControl inside of a ViewBox that has the Width and Height that you want the Path to display at like so:
<Viewbox x:Name="btnClose" Width="30" Height="30">
<ContentControl Content="{StaticResource someName}" />
</Viewbox>
Thats it! Another nice thing about using Paths is you can bind the Color of hte Background (Fill) or Foreground (Stroke). Taking our example here is how to control the Colors:
<SolidColorBrush x:Key="stForeColor" Color="#FFD4D7EA" />
<Canvas x:Key="someName" Width="100" Height="100">
<Path Fill="{StaticResource stForeColor}" Stroke="Transparent" StrokeThickness="1" Data="M92,8L92,8C103,18,103,35,92,45L45,92C35,103,18,103,8,92L8,92C-3,82,-3,65,8,55L55,8C65,-3,82,-3,92,8z"/>
</Canvas>
There are also tons of other things that you can do, anything that you can do with any other Shape, Effects, Animations etc.
精彩评论