WPF Path sizing problem - wpf

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.

Related

WPF OpacityMask weird behavior

I think I might have found a bug in WPF, or maybe I just don't fully understand how OpacityMasks work. As you can see on the first image, I have a diamond shaped path and a red circle. My goal is to make everything outside the diamond invisible. It works when the circle is inside the diamond shape, but whenever I move it to one of the corners, or outside the path, the OpacityMask seems to get stretched or distorted as you can see on the second image. My question is how can I make the OpacityMask work regardless of where the circle (or any other child element) is.
First image
Second Image
And this is my XAML code
<Grid Name="rootGrid">
<Grid>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=path}"/>
</Grid.OpacityMask>
<Path Name="path"
Stretch="Fill"
Fill="Cyan"
StrokeThickness="1"
Stroke="GreenYellow"
Data="M 0,-1 1,0 0,1 -1,0 0,-1"/>
<Ellipse Fill="Red"
Margin="20,-17,22,61"/>
</Grid>
</Grid>
Sorry for posting the images with links, but I don't have enough reputation to directly embed images.
Any help is appreciated!
Clemens solved my issue
Set ViewportUnits="Absolute" on the VisualBrush, and then set its Viewport property to an appropriate rectangle, e.g. Viewport="0,0,100,100"

Re-sizing WPF Geometry Path

I'm working on a WPF application. Given a geometry string path, such as:
F1 M 27,18L 23,26L 33,30L 24,38L 33,46L 23,50L 27,58L 45,58L 55,38L 45,18L 27,18 Z
Is it possible to scale the drawing to a width and height (no matter how small/large the original was) while keeping the figure as a whole, and then finally return the string path representation of the new scaled figure?
There is no need to scale the values in a path geometry string. Just put it in the Data property of a Path control and set its Width, Height and Stretch properties as needed:
<Path Data="F1 M27,18 L23,26 33,30 24,38 33,46 23,50 27,58 45,58 55,38 45,18 27,18 Z"
Width="100" Height="100" Stretch="Uniform" Fill="Black"/>
Yes you can do it! The only thing you need to do, is to use a Viewbox for wrapping the item. This is a sample with a code that I had done, in this case I used the geometry as a DrawingBrush
...
<UserControl.Resources>
<DrawingBrush x:Key="Field" Stretch="Uniform">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="{StaticResource FieldGrassBrush}" Geometry="F1 M 91.733,119.946C 92.8352,122.738 93.9374,125.529 92.9241,129.209C 91.9107,132.889 88.7819,137.458 84.4263,139.271C 80.0707,141.084 74.4885,140.142 70.8885,137.982C 67.2885,135.822 65.6707,132.444 65.1819,129.182C 64.693,125.92 65.333,122.773 65.973,119.626L 0.16,53.9203C 0.444319,53.4758 0.728638,53.0312 3.48413,48.7023C 6.23962,44.3733 11.4663,36.16 18.5596,28C 25.653,19.84 34.613,11.7333
........
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</UserControl.Resources>
...
Then the View Box (Note that the Grid has fixed Height and Width, but it will be stretched to the Viewbox's size, in this case, in an uniform way):
...
<Viewbox Stretch="Uniform" Grid.Row="2" Grid.ColumnSpan="2">
<Grid Height="300" Width="390">
<Rectangle Fill="{DynamicResource Field}" StrokeThickness="0"/>
...

resize text to largest possible size to fit a drawing rect with wraping

this are many similar questions asked to this but no good answer? i use context drawing to draw input text on a certain rect size "720,576", now i need to fit the whole text in that to the maximum font size, while maintaining the rows count?
i tried to create an equation to calculate that but no use. i even tried to loop size till text is clipped but i couldn't test that condition, and i searched for a week to see something similar but no avail!
lastly i tried to use a view box which is near what i want but it wont let the textblock inside to multiline as it always re-size the width contain all text in one line.
here is what i got:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="720" Width="576">
<Viewbox >
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center Text="how are you doining how are you doining how are you doining how are you doining how are you doining how are you doining how are you doining how are you doining how are you doining" " />
</Viewbox>
any help would be appreciated, thanks.
i cannot reproduce the behaviour you describe with just you example copy/pasted into a new project. It does write the text on several lines; Maybe you have a default setter on one object, maybe textbox...
OK very easy : set the width on your TextBox, so your TextBox will wrap. Maybe you wanna have as width the width of the ViewBox (ancestor binding).
I've had the same issue. Here is a fix:
<Viewbox StretchDirection="DownOnly">
<Grid MaxWidth="1000" MaxHeight="100">
<TextBlock TextWrapping="Wrap" FontSize="42" VerticalAlignment="Center" Text="very very very very very very very very very very very very very very very very long line"/>
</Grid>
</Viewbox>
So with extending the text line it will be resized till it is not so small, then it will be wrapped to next line

XAML Border vs Rectangle

I want to get a window like this
How should i get this and i am confused between Border and Rectangle in XAML.
Border is a container control aos its use is pretty simple to get, what is the use of ractangle as it can't contain any control in it.
Kindly help
Neither Border nor Rectangle will achieve this, you probably need a Path or something similar.
I would like to answer your last question
What is the use of ractangle as it can't contain any control in it.
Yes it can't contain any controls, but you can always draw it in the background
<Grid>
<Rectangle/>
<TextBlock/>
</Grid>
The user will propably don't see any difference. Why having two different Controls for that? Not sure, but my guess is that a Border is a shortcut to have a rectangle around a control, there might be more behind it, but thats how i see it. Also a Rectangle is a Shape, and it makes sense to have a Rectangle besides an Ellipse, Line etc.
You can draw a plygon as follow:
<Polygon
Points="100 50, 100 150,250 150, 250 75, 225 50"
Stroke="Black"
StrokeThickness="2">
<Polygon.Fill>
<SolidColorBrush Color="White"/>
</Polygon.Fill>
</Polygon>
Here you can find some other interesting example
This path is like your shape, if you use Expression Blend you can draw it into the editor.
<Path Data="M20.5,0.5 L153.81876,0.5 194.5,41.181242 194.5,103.5 C194.5,114.54569 185.5457,123.5 174.5,123.5 L20.5,123.5 C9.4543047,123.5 0.5,114.54569 0.5,103.5 L0.5,20.500004 C0.5,9.4543067 9.4543047,0.50000197 20.5,0.5 z" Fill="#FFF4F4F5" Stretch="Fill" Stroke="Black" HorizontalAlignment="Left" Height="124" VerticalAlignment="Top" Width="195"/>

Reuse of StaticResource in Silverlight 2.0

I am currently testing with Silverlight 2.0 Beta 2, and my goal is to define a resource element once and then reuse it many times in my rendering. This simple example defines a rectangle (myRect) as a resource and then I attempt to reuse it twice -- which fails with the error:
Attribute {StaticResource myRect} value is out of range. [Line: 9 Position: 83]
BTW, this sample works fine in WPF.
<UserControl x:Class="ReuseResourceTest.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="200" Height="200">
<Canvas x:Name="LayoutRoot" Background="Yellow">
<Canvas.Resources>
<RectangleGeometry x:Key="myRect" Rect="25,50,25,50" />
</Canvas.Resources>
<Path Stroke="Black" StrokeThickness="10" Data="{StaticResource myRect}" />
<Path Stroke="White" StrokeThickness="4" Data="{StaticResource myRect}" />
</Canvas>
</UserControl>
Any thoughts on what's up here.
Thanks,
-- Ed
I have also encountered the same problem when trying to reuse components defined as static resources. The workaround I have found is not declaring the controls as resources, but defining styles setting all the properties you need, and instantiating a new control with that style every time you need.
EDIT: The out of range exception you are getting happens when you assign a control to a container that already is inside another container. It also happens in many other scenarios (such as applying a style to an object that already has one), but I believe this is your case.

Resources