Understanding Stroke and StrokeThickness in relation to Height - wpf

Below I have a rectangle:
<Rectangle ClipToBounds="False" Grid.Row="1"
StrokeThickness="6"
StrokeDashArray=".5"
Height="2"
Stroke="Green" />
In the definition above, I have observed that to see the effect of StrokeDashArray=".5", I have to maintain the StrokeThickness between 6 to 9. If I give anything below 6 or above 9, I see a straight line.
I have two questions here:
In the above example, StrokeDashArray effect does not work below 6 and above 9. Why?
I think there is a relation between height and StrokeThickness. Can someone please explain?
When I read the MSDN Definition on stroke thickness it says:
Gets or sets the width of the Shape outline
I do not understand this.
Thanks in advance.

The stroke dash array is not expressed in pixels, it's dependent upon stroke thickness to get the final pixel value for each number in the array.
look here if you want to understand all the details behind it.
Generally speaking: The StrokeThickness tells WPF how thick
the line should be in pixels.
StrokeDashArray is a property that allows us to define the dashes and gaps in a
line. The values in the array alternate between the length of the dash and the
length of the gap. The values are also relative to the thickness of the stroke, so a
value of 1.0 means that the length of the corresponding dash or gap is equal to the
width of the stroke.
In your code you tell WPF to draw a dash that is 0.5 times of the width of the stroke but you didn't give the length of the gap, for example:
StrokeDashArray=".5 1"
This example says that the gap is equal to 1 time of the width.
Buttom line you need to define the gap for getting the wanted gap between the dashes.

Related

Hide UIElement but display that element's Effect

I am drawing a sequence of lines with DropShadowEffect in WPF. I want all the effects to display behind all the lines. So I drew the sequence twice: The first time I set Line.Stroke to Transparent and added the effect, and the second time I set Line.Stroke how I wanted and left off the effect. Unfortunately, a transparent stroke seems somehow to hide the effect as well, so my first sequence of lines doesn't display anything. Setting StrokeThickness to 0 also hides the effect. How can I show Line.Effect without showing the Line itself?
As commented, WPF - Element is invisible but has dropshadow effect asks the same question I do, but I want a code solution if possible, not just a Blend feature.
A dropshadoweffect is a sort of gaussian blur.
It takes pixels and sort of copies them offset.
You need something to copy.
So the answer is no, you cannot.
You could maybe try clipping the line to remove it, but I would think that's going to give you a hole where the line was.
In fact if you take the question as stated then the shadow is a blurred version of the line offset by a few pixels.
If your line is say:
<Line X1="0" X2="100" Y1="0" Y2="150"
StrokeThickness="5"
Stroke="Black">
<Line.Effect>
<DropShadowEffect/>
</Line.Effect>
</Line>
The shadow is something like:
<Line X1="4" X2="104" Y1="3" Y2="153"
StrokeThickness="5"
Stroke="Black">
<Line.Effect>
<BlurEffect/>
</Line.Effect>
</Line>
If this is not exactly what you want I could give you code which does a box blur on a black white image.
It's not as neat as gaussian but gaussian is hard to code. You'd have to paint your lines then blur em.
For interest
This is an explanation of the gaussian algorithm:
https://blog.en.uwa4d.com/2022/08/11/screen-post-processing-effects-chapter-1-basic-algorithm-of-gaussian-blur-and-its-implementation/#:~:text=Gaussian%20Blur%2C%20also%20known%20as,pixel%20values%20in%20the%20field.

WPF dashed line — fix dashes during resize

How can I avoid line dash resize during the window resize? Consider the following image:
http://leprastuff.ru/data/img/20130315/b83eea4a7a3f07ca53a0e118ddbb9230.Gif
<Rectangle SnapsToDevicePixels="True"
Stroke="DarkGray"
StrokeDashArray="10 10"
StrokeThickness="1" />
Thanks,
Tony.
Set the Height of the Rectangle to something greater than one.
Currently you're drawing the upper and lower border on top of each other. As the dashed stroke draws the Rectangle's border clockwise, starting at the top-left corner, you get the observed behaviour.
If you intended to draw a Rectangle with a height of one, you might better draw a Line instead.

Increase StrokeThickness but maintain dimensions of Path

I have a Path that normally has a StrokeThickness of 1. Under certain circumstances, it needs to have a StrokeThickness of 10. When I increase the stroke thickness, I don't want the path to take any additional space.
By default, just increasing the StrokeThickness increases the rendered size of the path. So you get something like this (the blue outline extends beyond the black boundary):
This is what I'm trying to achieve (the blue outline stays within the black boundary):
I can think of two mathematical ways to compensate for the increased StrokeWidth:
Manually adjust the points of the triangle inward.
Use a ScaleTransform on the Geometry of the Path.
Both of these would be somewhat problematic/complex. Is there an easier way?
You could clip the path by its own geometry like this:
<Path ... Clip="{Binding Data, RelativeSource={RelativeSource Self}}"/>
but then you would need to double the StrokeThickness, since only half of the stroke is visible.
On a whim I set StrokeThickness = -1 on my Rectangle and it did exactly what I wanted it to: the stroke goes on the inside of the Rectangle rather than on the outside.

How can I create beveled corners on a border in WPF?

I'm trying to do simple drawing in a subclass of a decorator, similar to what they're doing here...
How can I draw a border with squared corners in wpf?
...except with a single-pixel border thickness instead of the two they're using there. However, no matter what I do, WPF decides it needs to do its 'smoothing' (e.g. instead of rendering a single-pixel line, it renders a two-pixel line with each 'half' about 50% of the opacity.) In other words, it's trying to anti-alias the drawing. I do not want anti-aliased drawing. I want to say if I draw a line from 0,0 to 10,0 that I get a single-pixel-wide line that's exactly 10 pixels long without smoothing.
Now I know WPF does that, but I thought that's specifically why they introduced SnapsToDevicePixels and UseLayoutRounding, both of which I've set to 'True' in the XAML. I'm also making sure that the numbers I'm using are actual integers and not fractional numbers, but still I'm not getting the nice, crisp, one-pixel-wide lines I'm hoping for.
Help!!!
Mark
Aaaaah.... got it! WPF considers a line from 0,0 to 10,0 to literally be on that logical line, not the row of pixels as it is in GDI. To better explain, think of the coordinates in WPF being representative of the lines drawn on a piece of graph paper whereas the pixels are the squares those lines make up (assuming 96 DPI that is. You'd need to adjust accordingly if they are different.)
So... to get the drawing to refer to the pixel locations, we need to shift the drawing from the lines themselves to be the center of the pixels (squares on graph paper) so we shift all drawing by 0.5, 0.5 (again, assuming a DPI of 96)
So if it is a 96 DPI setting, simply adding this in the OnRender method worked like a charm...
drawingContext.PushTransform(new TranslateTransform(.5, .5));
Hope this helps others!
M
Have a look at this article: Draw lines exactly on physical device pixels
UPD
Some valuable quotes from the link:
The reason why the lines appear blurry, is that our points are center
points of the lines not edges. With a pen width of 1 the edges are
drawn excactly between two pixels.
A first approach is to round each point to an integer value (snap to a
logical pixel) an give it an offset of half the pen width. This
ensures, that the edges of the line align with logical pixels.
Fortunately the developers of the milcore (MIL stands for media
integration layer, that's WPFs rendering engine) give us a way to
guide the rendering engine to align a logical coordinate excatly on a
physical device pixels. To achieve this, we need to create a
GuidelineSet
protected override void OnRender(DrawingContext drawingContext)
{
Pen pen = new Pen(Brushes.Black, 1);
Rect rect = new Rect(20,20, 50, 60);
double halfPenWidth = pen.Thickness / 2;
// Create a guidelines set
GuidelineSet guidelines = new GuidelineSet();
guidelines.GuidelinesX.Add(rect.Left + halfPenWidth);
guidelines.GuidelinesX.Add(rect.Right + halfPenWidth);
guidelines.GuidelinesY.Add(rect.Top + halfPenWidth);
guidelines.GuidelinesY.Add(rect.Bottom + halfPenWidth);
drawingContext.PushGuidelineSet(guidelines);
drawingContext.DrawRectangle(null, pen, rect);
drawingContext.Pop();
}

WPF - UIElement.RenderSize not working for Line shape

I'm trying to make some drawing application and I get strange results in my "selection mode". If I draw a rectangle and "select it" RenderSize returns proper Size for it, but if Line is selected RenderSize returns Size which has Width set as Line.X2, and Height set as Line.Y2. For example: Line begins at X1 = 50, Y1 = 50, ends at X2 = 130, Y2 = 90, RenderSize returns Size with Width = 130 and Height = 90. My selection contains elements of type UIElement so I don't know (and really shouldn't care) what shape is selected in order to make my selection mode as generic as I can and I'd like to draw bounding box while user moves selected shape.
Tried google the problem but found nothing relevant so maybe you could help me with it. Is it because Rectangle has position set by Canvas while Line has its points set explicitly?
The reason you're getting 130x90 is because of the reason you cited. A Rectangle in WPF is position-less, it's just a height/width so the two size values are equal.
However a Line as defined by points necessarily defines a required offset from the origin, and thus the offset is included in the bounding box.
Also note that you can continue to use the Canvas.Top/Left properties with your Line object to further offset it, e.g.:
<Canvas>
<Line X1="50" X2="130" Y1="50" Y2="90" StrokeThickness="1" Stroke="Blue" Canvas.Top="50" Canvas.Left="30"></Line>
</Canvas>

Resources