How I can change a path position on my canvas. It don't moves by Canvas.Left/Top/Right/Bottom properties and it has not X, not Y.
If anyone know how it do, help me please.
For example: I have some path and I need change position for him programatically.
<Canvas>
<Path Stroke="Black" StrokeThickness="10">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="100,80">
<PathFigure.Segments>
<PathSegmentCollection>
<PolyBezierSegment Points="90,200 140,200 160,200 180,200 430,190 430,280" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
It doesn't have X nor Y properties, because a Path object does not really care much where it sits in.
You can place a Path inside a Canvas, or inside a Grid, or inside a StackPanel. Why would a Path need X/Y when it is put in a Grid that lays out the items with columns and rows? Same reasoning goes for Grid and its Columns and Rows. The Path does not have Grid-related Column or Row properties either, right?
Those settings are set by so-called attached properties. The Canvas define properties like Canvas.Left and Canvas.Top that you can use to position your Path inside Canvas. It works just the same way as Grid and Grid.Column/Grid.Row properties.
Actually, it's worth noting that Canvas provide you not with X/Y attached properties, but rather Left/Right and Top/Bottom, that allow you to choose where the position is aligned to.
XAML:
<Canvas>
<Path x:Name="mypath" Canvas.Left="50" Canvas.Top="25" .... />
</Canvas>
C#:
Canvas.SetLeft(mypath, 50.0);
Canvas.SetTop(mypath, 25.0);
Requirement: i have to come with a voltmeter user control.
The following code draws a arc, consisting of voltmeter ticks and values.
<Path Name="Pth" Stroke="DarkGray" StrokeThickness="2" Width="Auto" Height="48" Canvas.Top="12">
<Path.Data>
<PathGeometry >
<PathFigure StartPoint="35,28">
<ArcSegment IsLargeArc="False"
Size="23,75"
Point="200,28"
SweepDirection="Clockwise"
RotationAngle="90"/>
</PathFigure>
</PathGeometry>
</Path.Data>
As one one can see above, the pen used is stroke=dark gray. i am not talking about Fill property.
A section of the arc ,however, has to be rendered in blue(as alarm value) in response to user specified range.Any section of the arc .
The big picture is the predominant section of arc is rendered by gray pen and the alarm section is blue.
The problem as one can see above , we can assign the pen only to Path(not the arcsegment or geometry or pathfigure) and the path can use only one pen at a time .
A gradient brush won't work here as colors blend in.Here the two sections of the arc has to be clearly distinct by color from each other.
if i use two paths dividing the arc into 2arcsegments , i have to take of the various existing calculations ,which depends on the path geometry's fraction values.
what's the best approach for my case? any suggestions guys?
Taken from here:
One idea could be using a gradient brush with no spacing between the color blocks.
For example:
<Rectangle Width="200" Height="50">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Red" Offset="0.500"/>
<GradientStop Color="Lime" Offset="0.500"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Would result into something such as:
Although I'm afraid that you might have trouble finding the correct offset for it, in case it should be calculated dynamically.
EDIT: could you show us how the arc would approximately look like?
I have the following simple code that draws rectangle
<Canvas Name="MainImageLayer" >
<Image >
<Image.Source >
<DrawingImage xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" >
<DrawingImage.Drawing >
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="#FF1acc33" Thickness="1" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<SolidColorBrush>Red</SolidColorBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="300,480,287,83" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Canvas>
The result looks like this - note that the rectangle is in (0,0), even that the Rect is defined as
<RectangleGeometry Rect="300,480,287,83" />
I want it to start at (300,480), like that:
I can achieve that by inspecting my DrawingImage and doing :
<Canvas.Top>300</Canvas.Top>
<Canvas.Left>480</Canvas.Left>
But isn't there a better way, considering the fact that this data is encoded into the Geometry?
Your problem comes from the fact that you have all of your geometry wrapped in an 'Image' object. By default, the .Height and .Width properties of an Image object are set to 'Auto', and the .Stretch property is set to 'Uniform'. This guarantees that your rectangle will always appear in the top left corner of your Canvas.
If you really need to encapsulate your geometry in an 'Image' object (which I would caution you not to do) you will need to set the Margin of your Image object to 300,480,0,0. in order to get your rectangle to appear where you want it. This is required because of the way an 'Image' object handles its contents.
An Image object does not behave like a Canvas object, even if it is inside one.
Unless there is some overwhelming reason to keep the Image object, you will have much better success if you discard the Image and draw directly on the Canvas.
EDIT
Why should an Image object not be used in this case?
An Image object is primarily used for displaying...well, images, like bitmaps and such. It is not suited for drawing geometry in it at a specific location (and size). Like most WPF controls, it is what I would call a 'relative' control, meaning it is well suited for automatically resizing and positioning itself in relation to both its contents and its parent. A Canvas on the other hand is an example of an 'absolute' control. Its entire reason for existence is to allow content to be drawn upon it in an exact location with an exact size. Adding an Image inside a Canvas and then drawing the geometry inside the Image just adds an unnecessary layer of complexity between the Canvas and the geometry that needs to be drawn.
How can the geometry be added directly?
One of the easiest ways would be to use the Path object given in Clemens' answer. Just replace your entire Image object and all its contents with the 5 lines of that Path and your rectangle will appear exactly where it should be. You can also do it with a single line and a Rectangle object:
<Rectangle Height="83" Width="287" Margin="300,480,0,0" Stroke="#FF1acc33" StrokeThickness="1" Fill="Red" />
but I would recommend the Path since it contains the size and position of the rectangle in one set of numbers. The Path also allows you much more flexibility if you are working with shapes other than rectangles.
Seems like the DrawingImage (or the GeometryDrawing?) is somehow adjusted to its visible rectangle. Anyway, wouldn't this be a much simpler solution, instead of Image?
<Path Stroke="#FF1acc33" StrokeThickness="1" Fill="Red">
<Path.Data>
<RectangleGeometry Rect="300,480,287,83" />
</Path.Data>
</Path>
You can also add another non-empty GeometryDrawing that covers (0,0) (e.g. a RectangeGeometry with Rect="0,0,1,1") with a transparent brush to the DrawingGroup.
EDIT: As i understood, the DrawingImage is adjusted to Drawing.Bounds
I'm trying to figure out how to completely fill a custom geometry. It seems like it should be a common question, but I haven't really been able to find any solutions.
I have the following example geometry:
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry
Figures="M112,296C112,296 136,296 136,320 M112,344C112,344 136,344 136,320 M112,296L112,296 96,296 96,344 112,344"/>
</Path.Data>
</Path>
Which produces the following result:
This is the result I would like to see:
Any Ideas? I know I could make a single arc and that would resolve this particular case, but in my application the user can draw any type of geometry so the result could be composed of any number of "primitives" (PolyLineSegments, EllipseGeometries, ArcSegments, etc) and in the case that the resultant geometry contains some type of closed area, I'd like to accurately fill that area.
EDIT:
Here is an example of what a CombinedGeometry looks like if I ensure all three adjacent geometries overlap and create a unioned CombinedGeometry with the following code:
<Path Grid.Row="2" Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<PathGeometry
Figures="M111,293C111,296 136,293 136,325"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<PathGeometry
Figures="M111,346C111,344 136,344 136,320"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<PathGeometry
Figures="M125,296L115,296 96,296 96,344 120,344"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
And here is the result:
I was hoping it would union just the strokes and automagically figure out the correct filling for the new contiguous polygon... bummer.
EDIT 2:
Hmm so I think I've come up with a couple possible solutions, neither of which are as easy as I was hoping they would be. The first option would be to combine all of the geometries as above using a CombineGeometry structure, then I called "GetFlattenedPathGeometry" on the resultant "CombineGeometry" in order to get a PathGeometry. Next I iterate over each Figure in the PathGeometry and just remove those which are holes (which I think you should be able to do by getting rid of all the figures which are entirely contained by another, or holes may follow a convention of having either clockwise or counter-clockwise coordinates, not sure..), if all goes well you should be left with a single fully filled geometry.
The second option would be to again call "GetFlattenedPathGeometry" on whatever the resultant path is, so as to get a vertex based polygonal approximation of the path (without all the curve, arc, ellipse, etc notation, we want a path containing only points and lines). After that, you would just combine all the resultant figures/segments into a single figure whose segments are ordered either clockwise or counter-clockwise.
I've tested both approaches and they seem to work with at least the simple test case outlined above, though they will not work with more complex shapes (self-intersecting, concave, etc).. support which I require.. so the question remains, how do I do this??
EDIT 3:
Here is a more complicated geometry in which ordering/combining becomes more difficult:
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry
Figures="M104,160C104,160 72,160 72,136
M104,128C104,128 72,128 72,152
M152,232L152,232 152,216 120,216 120,160 128,160
M152,232L152,232 72,232 104,216 104,160 96,160
M104,128L104,128 168,128
M128,160L128,160 168,160
M165,160L168,160 200,128
M200,160L200,160 200,128
M200,160L200,160 168,128 152,128"/>
</Path.Data>
</Path>
Which produces:
Your example geometry is a combination of three adjacent shapes which do not overlap. The stroke is drawn on the outside edges simply because the shapes are not closed and the inner stroke lines do not exist. Although it may appear that the shapes are being merged and the stroke is applied to the geometry as a whole, that is not what is happening.
Closing the hole becomes a more complex problem of programmatically detecting the hole and closing it with the appropriate shape or by creating a new combined shape which doesn't have the hole (possibly by detecting and tracing the outer points). I am not aware of any functionality in WPF which can help you with this task. There does exist a CombinedGeometry class which can produce a union of two overlapping shapes. The shapes in this example are not overlapping.
Without context, it is hard to recommend a solution. If this is a free form drawing program, perhaps the user simply has to draw another shape in the middle to close the geometry.
Treat it as a "connect the points" problem :)
You have 5 points:
96,296 - top left point (corner)
112,296 - top - start of bezier curve
136,320 - far right - end of first bezier curve, start of second one
112,344 - bottom - end of second bezier
96,344 - bottom left point (corner)
And now, we shall connect them.
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry Figures="M112,296 C112,296 136,296 136,320 C136,320 136,344 112,344 M112,344 96,344 96,296 112,296"/>
</Path.Data>
</Path>
You can also use <GeometryGroup FillRule="Nonzero" ...> to fill your custom path from 1st post. Default is FillRule="EvenOdd", which produces filling like yours.*
See Path Markup Syntax (http://msdn.microsoft.com/en-us/library/ms752293.aspx) for more information on path mini-language.
Changed <Path> to <GeometryGroup>
EDIT 1:
I reordered your path a bit:
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry Figures="M72,152 C72,152 72,128 104,128 L168,128 200,160 200,128 168,160 120,160 120,216 152,216 152,232 72,232 104,216 104,160 C104,160 72,160 72,136"/>
</Path.Data>
</Path>
Which gives us:
New shape
Drawing path is like using some kind of "draw shape" tool in vector editing software, where you have to select next points, and when you don't have next one - shape is automatically closed (last point is connected to first one, but just for the purpose of filling shape - we can see how it's done, when we look at the 'nose' of new shape).
In your example You have 9 separate shapes (each one in separate line, where M specifies start of new figure), and they are all filled in the said-above-way.
Ofcourse, you can use GeometryGroup (with FillRule) or CombinedGeometry (with CombinedGeometryMode) to connect shapes.
I found an interesting article about creating shapes with wpf using a svg-file created with a graphic-tool like inkscape. This might give you some insight.
I need method for construct ring (Ńircle from which cut a circle of smaller radius) and return it as System.Windows.Shapes.Shape. Can I do this with Path? May be exist another ways?
Could you use an ellipse with a very thick stroke, but a transparent fill? Admittedly that doesn't work if you want the edges of the ring itself to be a different colour to the filled part...
Alternatively, I'd start looking at a Path containing two EllipseGeometry elements in a GeometryGroup with a FillRule of EvenOdd or a CombineGeometry with a GeometryCombineMode of Exclude. For example:
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="100" RadiusY="100" Center="125,125" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="125,125" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Produces this:
Am I right in saying that's what you were after?