I'm drawing a simple Path shows a simple triangle pointing down. This is what it's supposed to draw (shown on top of other stuff)
It works great except for one thing: No matter what value I give to the StrokeThickness, the triangle what shows up on my screen is the above picture. It never gets any thicker or thinner.
Here is the XAML. In this I have set the StrokeThickness to the absurd value of "200" just to see if anything would change. It didn't
<Path Grid.Row="0"
HorizontalAlignment="Right" VerticalAlignment="Top"
Width="30" Height="30" Stretch="Uniform"
StrokeThickness="200"
Data="{StaticResource ReplicaSurfacePathGeometry}"
Fill="White"
/>
This is the Geometry resource being drawn
<PathGeometry x:Key="ReplicaSurfacePathGeometry" FillRule="NonZero">
<PathGeometry.Figures>
m 600 1034.3 -501.52 -868.68
h1003
z
m 0 -120 397.6 -688.69
h-795.2
z
</PathGeometry.Figures>
</PathGeometry>
When I debug and use the live visual tree and look at the actual live properties of the path, it shows the StrokeThickness is indeed "200". But the line stays skinny.
I know this is something dumb on my part. What am I missing here?
The white line is the difference of the areas of the two triangle segments in the PathGeometry. It has nothing to do with a potential Stroke of the Path.
In order to make the StrokeThickness effective, set the Stroke property in conjunction with a more simple Geometry:
<Path Data="M0,0 L2,0 1,1.73 Z"
Width="30" Height="30" Stretch="Uniform"
Stroke="White" StrokeThickness="2"/>
Related
Hopefully I'm missing something simple here. I'm trying to draw arrow geometry using a Path shape in my XAML. However, since some of my lines go right to the edges of the bounding box, plus it having a thick stroke (made thicker for this demo) they are getting clipped.
I have tried setting ClipToBounds to false, but it doesn't have any effect.
Here's the relevant XAML. I've added coloring and a margin to show what it's doing..
<Border Background="LightGreen">
<Path
Margin="10" ClipToBounds="False"
Width="100"
Height="40"
Data="M0,20 L20,0 L20,12 L100,12 L100,28 L20,28 L20,40z"
Fill="Yellow"
Stroke="Black"
StrokeThickness="4" />
</Border>
And here's the results...
So how do you stop a Path shape from clipping?
The way I'd generate this display is to use the container (in your case the border) to control the size of the graphic, and then define the Path with Stretch="Fill" to use as much of the available space as appropriate. That way you can specify the Path's Data value using it's own nominal coordinate system, rather than calculating the exact pixel location for each vertex of the path.
<Border Background="LightGreen" Width="140" Height="60">
<Path
Margin="10"
Data="M 0,3 L 3,0 `L 3,2 L 10,2 L 10,4 L 3,4 L 3,6 z"
Fill="Yellow"
Stroke="Black"
StrokeThickness="4"
Stretch="Fill" />
</Border>
Ok, not exactly what I was afer (because I wanted the graphic to extend outside of the bounds) but a sort-of workaround was found here on S/O:
You clip the path by its own geometry, then you double the stroke thickness, like so:
<Path
Margin="10" ClipToBounds="False"
Width="100"
Height="40"
Data="M0,20 L20,0 L20,12 L100,12 L100,28 L20,28 L20,40z"
Clip="{Binding Data, RelativeSource={RelativeSource Self}}"
Fill="Yellow"
Stroke="Black"
StrokeThickness="8" /> <-- Doubled the thickness here
Notice how I had to double the StrokeThickness from 4 to 8.
The downside of this approach is you can only use it for closed paths that mark the bounds of your geometry. For instance, I couldn't draw a horizontal line down the center as part of the same geometry or it would be stroked at the full thickness of eight, not four, so you'll have to layer two different Path objects if you need such an effect.
I'm going to leave this as unanswered for a few more days in case someone does figure out how to disable clipping, but at least this is a semi-viable work-around for now.
I am trying to create a red circle with a black x through it using XAML.
My problem is that they aren't aligned correctly.
What is the right way to do this?
This is what I've got so far:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="Red">
<GeometryDrawing.Pen>
<Pen Brush="Transparent" Thickness="0"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<EllipseGeometry Center="8,8" RadiusX="8" RadiusY="8"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="2.5"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry>
<PathFigure StartPoint="4,4">
<LineSegment Point="12,12"/>
</PathFigure>
<PathFigure StartPoint="4,12">
<LineSegment Point="12,4"/>
</PathFigure>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Grid>
Simply putting an ellipse in the same grid with a black X the X isn't quite centered on the ellipse because the coordinates of each line you draw are really coordinates within the space allotted for it.
I think they needed to be in some sort of geometry or drawing aggregate to give them the same coordinate system. The geometry group and path are aggregators but both require their contents to have the same fill and stroke and the stroke and fill is different for the red circle (no stroke) and the black X (no fill).
The only aggregator that gives common coordinate systems and allows different fills & strokes for its members that I could find was the DrawingGroup.
The string shortcuts that work for creating a Path via its Data property don't appear to work for creating a PathGeometry so all had to be filled in by hand.
OK, so three hundred ways to skin a cat. Without fully understanding your use case I just came up with the fastest way to draw what you requested.
<Grid HorizontalAlignment="Left"
Height="80"
Margin="80,80,0,0"
VerticalAlignment="Top"
Width="80">
<Ellipse Fill="Red"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<Path Data="M40,53 L48,69 62,69 49,46 61,24 48,24 C48,24 40,39 40,39 40,39 32,24 32,24 L18,24 30,46 17,69 31,69 z"
Fill="Black"
Margin="15"
Stretch="Fill"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
This is probably outside what exactly you're looking for, but hopefully it at least gives you another way to think about it.
I had the same issue when trying to center text within an ellipse. The problem with using something like a TextBlock is that the kerning and escapement of each character is slightly different and so while the TextBlock element itself might be technically centered within the ellipse, this does not mean that the character will be centered in the ellipse. The character always appears to be too low and to the right of center in most situations.
I have had some success by wrapping the TextBlock in a ViewBox. While I am not fully versed in the technical implementation of the ViewBox, the ViewBox appears to wrap the visual rendering of the content which allows me to center that rendering more easily than trying to center to layout elements together.
I also seem to have better luck using an outer element that is of odd width/height rather than even width and height.
<Grid Width="19"
Height="19">
<Ellipse Fill="#FFB1413F"
StrokeThickness="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<Viewbox HorizontalAlignment="Center"
VerticalAlignment="Stretch">
<TextBlock Text="X"
Margin="1"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Viewbox>
</Grid>
I need to create a repeat symbol in XAML / WPF (procedural code would be ok too, though I'd prefer XAML if possible), something like the following, but I just need the not finished circle with the arrow (the white drawing in the button):
http://www.vista-style-icons.com/libs/phone/repeat.htm
I know how to create a circle in XAML, but I don't know how to create a not finished circle and add an arrow to the open end?
Thank you for any help!
You can create an unfinished circle by using an ArcSegment as a path segment in a Path shape. You specify the start and end point of the arc and the radius of the entire circle. You can render it on top of a blue circle by putting them in a grid:
<Grid Width="160" Height="160">
<Ellipse Fill="Blue"/>
<Path StrokeThickness="5" Stroke="White">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="115,45">
<ArcSegment Point="115,115" Size="50,50" IsLargeArc="True"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Polygon Points="115,115 105,105 125,105 125,125" Fill="White"/>
</Grid>
You can also use the shorter Path Markup Syntax to create a StreamGeometry rather than a PathGeometry:
<Grid Width="160" Height="160">
<Ellipse Fill="Blue"/>
<Path Data="M 115,45 A 50,50 0 1 0 115,115"
StrokeThickness="5" Stroke="White"/>
<Polygon Points="115,115 105,105 125,105 125,125" Fill="White"/>
</Grid>
You'll need to work on it to get exactly the look you want, but that should give you the basic technique for drawing an unfinished circle with an arrow.
I have a path shape that I would like to combine lines that have different line thicknesses?
The StrokeThickness property is set on the Path object so I cannot change it for different lines. This same issue would arise if I wanted to change my line color.
The reason I want to do this is so that I can draw an arrowhead. Charles Petzold arrowheads http://www.charlespetzold.com/blog/2007/04/191200.html don't work for me. If my line is dashed the closed arrowhead draws weirdly.
I figured a way to do it was to combine at the end of my path/line a new short line geometry that was thicker than the my original path/line and had TriangleLineCap, voila, got myself an arrowhead. But I can't combine geometries that have different line thicknesses and dashed types, etc.
Any ideas?
Just use multiple Path objects in a panel like a Canvas or a Grid where they will draw on top of each other:
<Grid>
<Path Stroke="Blue" StrokeThickness="2">
<Path.Data>
<EllipseGeometry Center="20 20" RadiusX="10" RadiusY="10" />
</Path.Data>
</Path>
<Path Stroke="Green" StrokeThickness="1" StrokeDashArray="1 2">
<Path.Data>
<LineGeometry StartPoint="10 20" EndPoint="30 20"/>
</Path.Data>
</Path>
</Grid>
One of the big problems with WPF is anti aliasing.
In fact, that's why UseLayoutRending was introduced in WPF 4.0.
However, it does not work for me in the following sample:
<StackPanel UseLayoutRounding="True" TextOptions.TextFormattingMode="Display" >
<Line X1="0" Y1="0" X2="200" Y2="0" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
<Line X1="0" Y1="1.5" X2="200" Y2="1.5" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
<Line X1="0" Y1="3.5" X2="200" Y2="3.5" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
<Line X1="0" Y1="7" X2="200" Y2="7" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
<Line X1="0" Y1="9" X2="200" Y2="9" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
</StackPanel>
The last two lines are still blurry.
(I am using Windows 7)
Any solution?
Or is it a bug in the beta of WPF 4.0?
Floele's answer showed the right direction, but the answer was not complete. Just set the y-values to half a pixel, e.g. Y1="7" -> Y1="7.5"
That's the reason the second and third lines are not blurred.
Getting lines to look sharp in WPF can be quite hard! And some times ... it seems like it takes a bit of black magic too!
I think that floele and Kimke's answers are pointing in correct direction. That is, often times you will want to put single pixel lines on a 0.5 pixel boundary ... given the way that it draws the line (half on one side and half on another).
However, it isn't always that simple either. For example, it also depends on the surrounding xaml. For example, try this code out and resize when you do:
<Canvas HorizontalAlignment="Center" VerticalAlignment="Center">
<Line X1="0" Y1="5" X2="200" Y2="5" StrokeThickness="1" Stroke="Black" UseLayoutRounding="True"/>
<Line X1="0" Y1="15" X2="200" Y2="15" StrokeThickness="1" Stroke="Black" UseLayoutRounding="True"/>
</Canvas>
Then, try this code out (again, resize when you do):
<Canvas HorizontalAlignment="Center" VerticalAlignment="Center" UseLayoutRounding="True">
<Line X1="0" Y1="5" X2="200" Y2="5" StrokeThickness="1" Stroke="Black"/>
<Line X1="0" Y1="15" X2="200" Y2="15" StrokeThickness="1" Stroke="Black"/>
</Canvas>
The only difference between the two snippets is that the first uses UseLayoutRounding on the Lines while the second uses UseLayoutRounding on the Canvas container (which then also property inherit to the Lines).
However, that difference yields some interesting results. When UseLayoutRounding is used on the container the single pixel lines consistently stay spread out over 2 pixels and they don't move around. When UseLayoutRounding is used on the Lines directly, and you resize, the lines will sometimes be 1 pixel sharp ... and other times will be spead over 2 pixels.
And that brings me to the sample xaml in the original question. A few comments on it:
First off, you should realize that both UseLayoutRounding and SnapsToDevicePixels property inherit. That is, if you use it on the layout container it will inherit to the items in the layout container.
UseLayoutRounding and SnapsToDevicePixels shouldn't necessarily be used together. They can be ... but I would normally try using them separately ... either one or the other. More info here: When should I use SnapsToDevicePixels in WPF 4.0?
TextOptions.TextFormattingMode options affect text, not lines.
That StackPanel that you are using as the layout container could also affect how the lines are being laid out. Canvas allows you more precise positioning control of your lines. StackPanel will just layout one line after the other line ... and might yield some unexpected results.
More info than what the original poster was wanting. However, I personally know how tricky it is to get lines sharp in WPF. Hope this info helps someone!
The reason is apparently simpler. I only found an explanation in "Pro WPF in VB 2010" and not on MSDN though: http://books.google.de/books?id=F-gMZkAlUDUC&pg=PA334&lpg=PA334
In short, the StrokeThickness will be divided for both sides of a shape, so a StrokeThickness of 1 equals a thickness of 0.5 for each side, and since the line only has one side, it will be 0.5 units thick and thus appear blurry even when using SnapsToDevicePixels=True. So simply use "2" as StrokeThickness.
You can verify this by using a StrokeThickness of 4, the line will have a thickness of 2 then, which is more than an "accidential" single pixel deviation.
Have you tried to change the TextOptions.TextFormattingMode property to Display ? See this post from Lester Lobo for details