Getting points along a XAML Shapes.Path - c#

I'm trying to get points along a Shapes.Path object from a Silverlight MainWindow.xaml file. I use Expression Blend to create the path.
The following code works fine unless I try to move the path in expression blend, which only moves the margins. Apparently that doesn't get calculated in, and I get the points from where the path was originally created.
var pathGeometry = path.Data.GetFlattenedPathGeometry();
var currentPoint = new System.Windows.Point();
var tangentPoint = new System.Windows.Point();
var rList = new List<Point>();
var pointsCount = 10;
for (int i = 0; i < pointsCount; i++)
{
double fraction = (double)i / (double)(pointsCount - 1);
pathGeometry.GetPointAtFractionLength(fraction, out currentPoint, out tangentPoint);
rList.Add(currentPoint);
}
return rList;
Any suggestions on how to get the margin change to also calculate in?

Ah... I think I see the problem. PathGeometry does not inherit from UIElement, meaning that it does not have any sense of Margins. The Geometry is hosted within a control that does, and it is that control which actually draws the Geometry onto the screen. If you want to translate a point on the Geometry into screen coordinates, then you'll need to find the location of the host control and calculate the offset.
My guess is that your PathGeometry is hosted inside a Path control. (Confusing, no?) Getting the location in screen coordinates is relatively easy. You can call the PointToScreen method on Path and feed it the calculated point (perhaps adding any padding you may be applying). Getting a point relative to the window is more complicated. You could call PointToScreen (for point 0,0) on the window and then subtract the Path's point from the window's. Or, you could put the Path inside a Canvas (where the Canvas is directly inside the window) and simply get the Canvas.Top and Canvas.Left.
You don't say what your ultimate goal is, so it is difficult to recommend a particular solution for you.

Related

Get the position of a view on the screen

I am using Xamarin native (not forms) and I have an ImageView inside a RelativeLayout.
Is there any way to get the absolute position of the view relative to the screen and not relative to the parent?
I saw some solutions for this problem on Xamarin.Forms that loop through the parent view and add its coordinates relative to its parent, but it doesn't seem to work here.
I tried using iv.GetX() but this is for getting the position in relative to the parent.
What is the way to get the absolute position?
Thanks in advance.
You could get the absolute coordinates across the entire screen with the GetLocationOnScreen method.
like:
int[] location = new int[2];
iv.GetLocationOnScreen(location);
int x = location[0];
int y = location[1];
Note:
This value is calculated from the top of the screen, which includes the height of the notification bar.If those parameters are printed in the Activity's OnCreate() method, they are all 0, and you won't get them until the UI controls are loaded.
It's better to get it in OnWindowFocusChanged method.

Label disappearing after attributting it a parent [duplicate]

In my GUI, i need to change Parents of PictureBoxes to the background PictureBox so that the transparency works correctly.
However, changing the parent also changes the location of the pictureboxes. I have tried grabbing the absolute location of the picture box via the PointToClient, but it doesn't work. I put the coordinates in the comments, and they don't change after assigning the new parent even though the image visibly changes location. Furthermore, I don't expect that it could possibly work as it's being passed a point, not an object with more information about parents and whatnot that's needed to deduce the absolute position.
What is the correct way to deduce the absolute position of an element so that I can move the image to the correct location after its parent changes? Or is there a better way to do this?
Point oldRel = pictureBox4.Location; //258, 109
Point oldAbs = PointToClient(oldRel); //75, -96
//Commenting out this line fixes the image shift but ruins the transparency
pictureBox4.Parent = pictureBox2;
Point newRel = pictureBox4.Location; //258, 109
Point newAbs = PointToClient(pictureBox4.Location); //75, -96
This will move a Control child from one Parent to a new one, keeping the absolute screen position intact:
void MoveTo(Control child, Control newParent )
{
child.Location = newParent.PointToClient(child.PointToScreen(Point.Empty));
child.Parent = newParent;
}
The trick with PointToClient and PointToScreen is to use them from the right parent control; not setting the control will default to the Form, which will miss out on the actual position of the parent..

WPF Canvas Only Extends to the Right?

I'm making a WPF application using C# and I am in the stage of developing more UI for it. I have all my components such as TextBlocks and other Canvases inside of a main Canvas, but if I drag the left side of it to make more room, it expands on the right side, meaning I have to move everything over. The same thing happens when I try and extend the top; The bottom gets bigger. I have the RenderTransformOrigin of the Canvas set to 0,0 so I'm not quite sure what is going on. Any help is appreciated.
Thanks!
By default, a Canvas doesn't have a Width or Height, its actual size depends if you set those properties explicitly, or if you set HorizontalAlignment or VerticalAlignment to Stretch.
In either case, elements inside of a Canvas are aligned relative to its top-left corner, except if you explicitly set their Canvas.Top, Canvas.Left, Canvas.Right and/or Canvas.Bottom properties.
You could also position elements containing a Geometry (like derivatives of Shape, Path being a common example) by their absolute Geometry coordinates. Actually this already is the way they are rendered.
Regarding your comment in the question, usually you don't change where things align relative to the Canvas, since it is much easier (I think even recommended) to reposition the canvas itself, so that everything it contains would be repositioned too.
Common ways to do that would be adding elements to a Canvas, and then position that Canvas inside another "parent" Canvas, similar to "grouping" in vector design programs like CorelDraw/Inkscape/Illustrator.
Another way would be to use RenderTransform property of Canvas containing the elements you want to reposition.
After you get used to see Canvas as a "coordinate system", you can and should think of Canvas-inside-Canvas-inside-Canvas-... as a way of hierarchically grouping things that share the same cartesian space, as opposed to Panels and ContentControls, which are nested inside one another and cannot typically share space.
Like I said in my comment, all the controls inside the canvas are positioned relative to the canvas' top left point. If you want them to retain their "position" when the canvas is resized to the left or top, you will need to offset them yourself. You could do this with a utility method that you call whenever you want to resize the canvas:
public static void StretchWidthLeft(Canvas canvas, double newWidth)
{
double diff = newWidth - canvas.Width;
canvas.Width = newWidth;
foreach (UIElement child in canvas.Children)
{
Canvas.SetLeft(child, Canvas.GetLeft(child) + diff);
}
}
public static void StretchHeightTop(Canvas canvas, double newHeight)
{
double diff = newHeight - canvas.Height;
canvas.Height = newHeight;
foreach (UIElement child in canvas.Children)
{
Canvas.SetTop(child, Canvas.GetTop(child) + diff);
}
}
Note that these methods do not reposition the canvases themselves. How you would do that depends on how the canvas' parent is maintaining their position.

Confused about drawing a rectangle in WPF

I want to draw a rectangle on a canvas in WPF. For drawing a line I can do this:
line.X1 = ls.P0.X;
line.Y1 = ls.P0.Y;
line.X2 = ls.P1.X;
line.Y2 = ls.P1.Y;
MyCanvas.Children.Add(line);
...in other words the location is a property of the line itself. I want to draw a rectangle the same way, i.e., assign its coordinates and add it to my canvas. But the examples I've seen online so far seem to look like this:
rect = new Rectangle
{
Stroke = Brushes.LightBlue,
StrokeThickness = 2
};
Canvas.SetLeft(rect,startPoint.X);
Canvas.SetTop(rect,startPoint.X);
canvas.Children.Add(rect);
...in other words it doesn't look like the rectangle has an inherent location, but instead its location is set by calling a method of Canvas. Is this true - Lines have inherent coordinates but Rectangles do not? Is there any way to have a rectangle in WPF with an inherent location, like a line, or do I have to roll my own (using lines)?
You could use a Path control with a RectangleGeometry like this:
var rect = new Path
{
Data = new RectangleGeometry(new Rect(x, y, width, height)),
Stroke = Brushes.LightBlue,
StrokeThickness = 2
};
canvas.Children.Add(rect);
...in other words it doesn't look like the rectangle has an inherent location, but instead its location is set by calling a method of Canvas. Is this true - Lines have inherent coordinates but Rectangles do not?
Locations in WPF are relative, which begs the question: coordinates relative to what?
Line, Rectangle, and Path all inherit from Shape, and in the case of any Shape object, the coordinates of the defining geometry are relative to the top-left corner of the Shape itself. Thus, when you create a Line object from (100, 300) to (300, 100), the resulting element is 300x300 points in size, even though the visible line has bounds of 200x200:
In this case, it is unnecessary to place the Line within a Canvas, as you are not using the coordinate system of the Canvas.
Some shapes like Line and Path allow you to place geometry at any "internal" coordinates you like. Others, like Rectangle and Ellipse, always position their defining geometry at (0, 0) internally, forcing you to use other layout properties to position the shapes within the greater scene (e.g., Canvas.Top/Left, Margin, etc.).
In your example, if you were to define a Rectangle of 200x200 points, and use the Canvas attached properties to position the rectangle at (100, 100), the resulting Rectangle element would measure 200x200, while the parent Canvas would measure itself to be at least 300x300, which is arguably more intuitive:
(shading added for clarity)
You are correct in that this is rather inconsistent. You may find it useful to always use layout properties (e.g., Canvas.Left/Top) to position shapes within a scene such that all elements are using the same coordinate system.

How can I predict a XAML Label's height on a Canvas

I have an application where I need to dynamically build the content to a Canvas. Everything works just fine, but I am a little unsure of how I can set the y coordinates for the labels in the safest way. For example, I need to add three labels that are essentially lines of text. In Java Swing or C# GDI I would just query the the font for the line height and add that value to the y coordinate of the drawText command.
This is my code.
double y = 0.0;
_line1.Content = "Line1";
_line1.SetValue(Canvas.TopProperty, y);
_line1.SetValue(Canvas.LeftProperty, 0.0);
CanvasChart.Children.Add(_line1);
double textHeight = _line1.Height;
y += textHeight;
_line2.Content = "Line2";
_line2.SetValue(Canvas.TopProperty, 0.0);
_line2.SetValue(Canvas.LeftProperty, y);
CanvasChart.Children.Add(_line2);
This does not work because _line1.Height does not seem to be set to anything useful at this point. I suppose it has not rendered yet. The above code is in the loaded event for the window. ActualHeight does not help either.
Most code that I've seen seems to just set them to a hard coded value. That I suppose looks right on the developer's display, and you just hope looks good at other resolutions/DPI. In Swing and GDI I always had the best results finding out exactly how many pixels a string will be rendered at and using this to offset the next line.
You must call the Measure method, specifying an infinite available size. This will update the DesiredSize of the control:
_line1.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double textHeight = _line1.DesiredSize.Height;
Another easy way to achieve the desired effect is to put the labels in a StackPanel.
In Swing and GDI I always had the best results finding out exactly how many pixels a string will be rendered at and using this to offset the next line.
This is possible in WPF as well. The GlyphTypeface class provides the AdvanceWidths and AdvanceHeights properties for each character in a typeface. By using CharacterToGlyphMap, you can map a character to an index within the AdvanceHeights, and use that to determine the actual height of any character.
For a detailed example, see GlyphRun and So Forth.

Categories

Resources