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
Related
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"/>
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.
Take a look at this picture:
Notice how the line to the left has a clean corner, and the one to the right does not. Here's the XAML I used to create each:
<Canvas Margin="10">
<Polyline Stroke="Green" StrokeThickness="10">
<Polyline.Points>
<Point X="0" Y="0"/>
<Point X="30" Y="50"/>
<Point X="60" Y="0"/>
</Polyline.Points>
</Polyline>
<Line X1="100" Y1="0" X2="130" Y2="50" Stroke="Red" StrokeThickness="10"/>
<Line X1="130" Y1="50" X2="160" Y2="0" Stroke="Blue" StrokeThickness="10"/>
</Canvas>
Obviously the Polyline handles the corners easily, but it doesn't support segments with different colors, which is what I need. Creating separate lines solves this problem, but I lose the clean corners. I need to be able to create a line with the corners of line 1, but the colors of line 2.
My actual application is a line graph, so these lines are created dynamically and could have dozens of segments each with an angle ranging from 0 to 180 degrees.
The only solution I have come across is to use a LinearGradientBrush with Polyline.Stroke, and put in gradient stops where the corners should be. But considering the length of the line and each segment are totally variable and the gradient stops seem to be measured in percentages (from 0 at one end to 1 at the other) it just seems like it would be a pain to calculate and comes off as an over-complicated solution. Is there any simpler way to do this?
Set the start and end caps of the Lines' stroke to Round:
<Line X1="100" Y1="0" X2="130" Y2="50" Stroke="Red" StrokeThickness="10"
StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
<Line X1="130" Y1="50" X2="160" Y2="0" Stroke="Blue" StrokeThickness="10"
StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
Are horizontal or vertical WPF Lines limited to 125,000 pixels? Looking at the following code the Green line displays correctly but the Red one does not display at all despite being just 0.01 longer. Any idea why?
<Window x:Class="DCView.Window11"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window11" Height="300" Width="300">
<ScrollViewer>
<Grid Width="150000">
<Line X1="0" X2="125000.00" Y1="10" Y2="10" StrokeThickness="1" Stroke="Green"></Line>
<Line X1="0" X2="125000.01" Y1="20" Y2="20" StrokeThickness="1" Stroke="Red"></Line>
</Grid>
</ScrollViewer>
</Window>
Andrew
This seems to be a limitation in WPF's handling of vector graphics.
To make it more complicated, try changing the StrokeThickness - if you set the StrokeThickness of your red line from 1 to 2, it displays again... until you increase the length above 250000.. Then it vanishes again.
<Grid>
<Line X1="0" X2="125000.00" Y1="10" Y2="10" StrokeThickness="1" Stroke="Green"></Line>
<Line X1="0" X2="250000.00" Y1="20" Y2="20" StrokeThickness="2" Stroke="Red"></Line>
<Line X1="0" X2="250000.01" Y1="30" Y2="30" StrokeThickness="2" Stroke="Blue"></Line>
</Grid>
The max length goes up as you increase your stroke thickness.
Also Note that if the line wasn't perfectly horizontal or vertical, the length limit seems to vanish:
<Grid>
<Line X1="0" X2="125000.00" Y1="10" Y2="10" StrokeThickness="1" Stroke="Green" />
<Line X1="0" X2="125000.01" Y1="20" Y2="20.0001" StrokeThickness="1" Stroke="Red" />
</Grid>
You can find the bug written up on connect: Disappearing Path (WPF)
It definitely draws past 150,000 pixels, It is a bit strange that the line is not seen in this case, because for example if you do this
<Line X1="0" X2="125000.01" Y1="20" Y2="20" StrokeThickness="2" Stroke="Red"></Line>
or this
<Line X1="0" X2="125000.01" Y1="21" Y2="20" StrokeThickness="1" Stroke="Red"></Line>
all works fine, There is probably a answer somewhere as to why, but good find as this would cause significant flicker if you were animating the value of X2.
Creating a relatively simple data entry form, and just want to separate certain sections with a horizontal line (not unlike an HR tag in HTML) that stretches the full length of the form.
I have tried this:
<Line Stretch="Fill" Stroke="Black" X2="1"/>
Because the parent control is not a fixed width, this line causes the window to stretch to the full width of the screen.
Is there an easy way to do this without fixing the width of my parent control/window?
How about add this to your xaml:
<Separator/>
I had the same issue and eventually chose to use a Rectangle element:
<Rectangle HorizontalAlignment="Stretch" Fill="Blue" Height="4"/>
In my opinion it's somewhat easier to modify/shape than a separator.
Of course the Separator is a very easy and neat solution for simple separations :)
Use a Border of height 1 and don't set the Width (i.e. Width = Auto, HorizontalAlignment = Stretch, the default)
For anyone else struggling with this: Qwertie's comment worked well for me.
<Border Width="1" Margin="2" Background="#8888"/>
This creates a vertical seperator which you can talior to suit your needs.
To draw Horizontal
************************
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Center" Fill="DarkCyan" Height="4"/>
To draw vertical
*******************
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Center" Fill="DarkCyan" Height="4" Width="Auto" >
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="90"/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
1st, the op asked for a horizontal line. seperators are vertical (not sure how that got so many up votes---they're not horizontal)
Play around using a simple line tag
<Line
Grid.Row="4"
HorizontalAlignment="Center"
StrokeThickness="1"
X1="0"
X2="300"
Y1="10"
Y2="10" />
That's a horizontal line. Play around with the X & Y axis to make it work...simple enough to make a horizontal, vertical or any other angled line segment that doesn't resize a window