How to move around a Path object inside Canvas? - c#

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);

Related

How to clip an element fron another one in WPF? [duplicate]

Anyone know a good way to create this object from Xaml? It also has to work at .5 Opacity when layered on top of other controls.
It also has to be resizable via Horizontal or Vertical Alignment.
I'm having some difficulty. The closest I get is with 2 borders, one having a negative margin--but it doesn't work when Opacity is applied.
Code that works:
<Path Fill="Black">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry RadiusX="5" RadiusY="5" Rect="0,0,200,100" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry RadiusX="5" RadiusY="5" Rect="105,5,90,90" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Use a GeometryGroup with an EvenOdd FillRule, or a CombinedGeometry with a GeometryCombineMode of Xor or Exclude. The geometries to combine will both be RectangleGeometry objects, with an appropriate RadiusX and RadiusY. The result will be the outer rectangle with a "hole" in it where the inner rectangle was located. (I assume this is what you want rather than a white rectangle within the black one.)
You can then assign this composite geometry to a Path as its Data property, and set the Fill and Opacity as required.

How to override all Shapes in a specific area WPF

Currently I am filling my MainWindow with a slightly transparent black:
But I want it to have a "hole" where this effect doesn't take place which should look like the following:
So this needs to be done at runtime since the area which the hole represents is going to change multiple times while the program is running.
What I thought I could do
So at first I thought I could just cut the area in the middle out
like you could do with a Graphics object, but the slightly
transparent black is nothing but a rectangle which is added as a child on a canvas which is currently done like this:
var background = new System.Windows.Shapes.Rectangle
{
Fill = new SolidColorBrush(System.Windows.Media.Color.FromArgb(150, 0, 0, 0)),
Width = ScreenInfo.Width,
Height = ScreenInfo.Height
};
MainCanvas.Children.Add(background);
But I couldn't fine any way to achieve this cut effect.
Creating 4 Rectangles which would look something like this: but this way of doing it didn't seem to me as the most effecient way of achieving this.
Thanks for any kind of help!
Create a CombinedGeometry by cutting a smaller square out of a larger one and then use that with a path. How you size it will depend on your application, a Viewbox will probably be good enough for most cases:
<Grid>
<TextBlock Text="Hello World!" FontSize="200" Foreground="Red" TextWrapping="Wrap" TextAlignment="Center"/>
<Viewbox Stretch="UniformToFill">
<Path Fill="#C0000000">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,4,4" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry x:Name="cutRect" Rect="1,1,2,2" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Viewbox>
</Grid>
Then to change the size of the inner geometry you can either bind its Rect to a view model property or change it directly in code-behind:
cutRect.Rect = new Rect(1, 1, 1, 1);

Why is a GeometryDrawing displayed on Canvas with clipped coordinates?

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

Alternative to absolute positioning of shapes' points

I was wondering if there was a way in WPF to create a shape (ex.: polygon, line, ...) but instead of using absolute positioning for the points of the shape, we could use something like percentage.
For example, instead of having a line with absolute positioning like this: (X1=0,Y1=50,X2=100,Y2=50), we could have a line with percentage values (0 to 1) like this (X1=0,Y1=0.5, X2=1, Y2=0.5 where 1 is equivalent to the size of the parent). So no matter what is the size of the parent of the shape, the shape would always be proportional to its parent.
That could be done with dependency properties, but I would find it much cleaner if there was a way to do it with something like I described. I hope I didn't miss something really obvious that does exactly that...
Thanks!
You could achieve a similar effect if you scale it by applying a scale transform on your geometry the size of the control.
<Path Width="100" Height="100" Stroke="Red">
<Path.Data>
<LineGeometry StartPoint="0 0" EndPoint="1 1">
<LineGeometry.Transform>
<ScaleTransform ScaleX="{Binding Path=Width, RelativeSource={RelativeSource FindAncestor, AncestorType=Path}}"
ScaleY="{Binding Path=Height, RelativeSource={RelativeSource FindAncestor, AncestorType=Path}}" />
</LineGeometry.Transform>
</LineGeometry>
</Path.Data>
</Path>
This should draw a red line with absolute points (0, 0) to (100, 100).

Construct a shape in the form of a ring on C#

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?

Categories

Resources