Hi I have created an UML diagram in C# with nodes and edges. The nodes are connected to the other nodes with edges, but I want to add text to the edges.
To draw the edges I have use Bindings to startNode and endNode (simple).
<Line Stroke="Black" X1="{Binding StartNode.Center_x}" Y1="{Binding StartNode.Center_y}" X2="{Binding EndNode.Center_x}" Y2="{Binding EndNode.Center_y}" StrokeThickness="5"/>
It is simple to add text before or after the edge, but if I do so, it is overidden by the connected node.
My question is, how do I add a label or textbox in the middle of the line?
Do I need to create two lines, one at the beginning and before the text, and the second after the text and to the end? And if so, how to?
I think a better way to approach your problem is to have a single UI Container like a Panel, and then inside the container you draw the line and put the text as you wish, that may help you to overlay the multiple objects dilemma.
Related
I just started using Geomectry Drawing in xaml and I came across this interesting article https://msdn.microsoft.com/en-us/library/aa480159.aspx. Here, I find out that the following drawingbrush gives a graph diagram as an output.
<DrawingBrush x:Name="gridBackgroundBrush"
Viewport="0,0,10,10"
ViewportUnits="Absolute"
TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z" Brush="Green" />
<GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="Green" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
From further reading, I figure out that M means starting point or move to, L means line and Z means close but could not figure out how this will give me two lines - one horizontal and one vertical? Any help understanding this will be highly appreciated. Thanks.
So here's a quick break down for you.
Your example is a DrawingBrush which is explicitly set to TileMode="Tile" which is the equivalent of repeat-x/repeat-y if you're more familiar with CSS as example. So it's instructed to repeat itself up-down/left-right repeating.
Your two bits of your Geometry Drawing if translated individually are two squares with one stretching vertical, one stretching horizontal. While your explicitly set ViewportUnits is dictating the size and position effectively making repeated columns and rows.
Your Path Geometry uses Path Markup Syntax to draw these lines as you pointed out. For a more visual explanation replace your Brush on each.
<GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z" Brush="Red" />
<GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="Blue" />
...and voila! You have yourself a repeated line background with effective vertical/horizontal columns creating your grid. See links for more detail and hope this helps. Cheers!
Oh, and the question you got that from also had a link that would have shed a little light on it but not as much as you'd like so I didn't mark this duplicate.
ADDENDUM:
A little more clarification. Though if you want to learn more I'd follow the documentation link someone spent a lot of time writing to answer this already. Think if you have an x,y grid you're drawing points on.
Say you're using the pen tool in something like Adobe Illustrator, or Blend. Your first click is setting your M as your start point based on the relative size of the container. Then you click in another spot...well now you effectively have a L line.
So when we see: M0,0 L1,0 that's our first starting Line. In this case it's the top corner stretching to the right corner since there's not another anchor in the line between the two points. The next set acts as the anchor to tell that line to change it's direction to make the side, and so on, and so fourth until you hit the end at Z. Hope this helps but I would encourage the documentation first.
Here they are individually if you feel like tinkering with numbers and learning:
<Path Data="M0,0 L1,0 1,0.1, 0,0.1Z"
Height="150" Width="150" Stroke="Red" />
<Path Data="M0,0 L0,1 0.1,1, 0.1,0Z"
Height="150" Width="150" Stroke="Blue" />
I came cross the same thing when I used GMaps V3. It is an SVG path notation path that allows you to draw on WPF and on browsers too. You can find the complete documentation in the link.
I'm trying to draw a graph-paper like grid as the background of a Canvas. This grid is different from most explanations of how to do this that I've found because the canvas can be scaled to implement zooming. What I want to do is have a series of scales of grid lines, i.e. at every 10^n units. Then, the grid lines should fade out as they become close together due to zooming. In other words if n is large, the lines associated with that grid should be darker/heavier weight than those for a smaller n.
This was easy to do in WinForms, I implemented it by overriding OnPaint and defining the color of the line to be a function of the distance to the next grid line. Lines far apart were given a heavier weight than lines close together.
I have not figured out how to do this in WPF. I can sort of get this behavior by creating a line that has a StrokeThickness according to the spacing of the grid lines, but this only works for a small range of StrokeThickness and scaling values. It would work if it were possible to define a line as having a very heavy weight, but still a small StrokeThickness.
Even doing this via implementing a custom control with OnRender is difficult because I have not found a reliable way to get the scale of the control while rendering it (the ScaleTransform is part of one of the parent controls, not the immediate parent).
Any thoughts on how to accomplish this goal would be much appreciated!
I solved this by NOT adding the grid to the canvas but by stacking the canvas on top of another control that contains the grid:
<Grid>
<Canvas x:Name="GridLayer"/>
<Canvas x:Name="DrawingLayer" />
</Grid>
When zooming events occur I simply redraw the GridLayer.
This allowed me to only draw the lines that are needed, to draw them exactly how I want them and, in my case very important because I had potentially a gazillion grid lines, I did not need to draw the lines any longer/taller than needed. This way I conserved a lot of CPU time.
Another thing to note is that I implemented my own zoom code. I did not use a RenderTransform or a ViewBox because I wanted the line to stay at the same width. All I did was keep track of the coordinates of the top left corner to support panning and the zoomlevel. As soon as one of these changes I redraw the canvases. I wrote two functions: one transforms a coordinate on the Canvas to a graph coordinate and the other one does the reverse. The first method allows me to translate cursor coordinates to graph coordinates and the second one will turn the coordinates of the graph into points that can be used to draw on the canvas.
Untested code and making a lot of assumptions about the orientation of axis:
Point Graph2Canvas(Point graphPoint)
{
var canvasPoint = new Point(graphPoint);
canvasPoint.X *= zoomLevel;
canvasPoint.Y *= zoomLevel;
canvasPoint.X -= topLeft.X;
canvasPoint.Y -= topLeft.Y;
return canvasPoint;
}
This can be optimized and the truth is I created more functions that do the same thing for collections of points.
Extra:
I ended up with a far more complex setup that looked a bit like this:
<Grid>
<Canvas x:Name="BackgroundLayer"/>
<Canvas x:Name="GridLayer"/>
<Canvas x:Name="AxisLayer"/>
<Canvas x:Name="DrawingLayer" />
<Canvas x:Name="SelectionBoxLayer"/>
<Canvas x:Name="CursorLayer"/>
</Grid>
I have a Canvas with 2 "dots" drawn on it. See this (simplified) code:
<Canvas>
<Ellipse />
<Ellipse />
<Canvas.RenderTransform>
<RotateTransform x:Name="rotateEllipse" />
</Canvas.RenderTransform>
</Canvas>
As you can see, I want to rotate the canvas using the given RotateTransform.
Next, I want to put a TextBlock near to each Ellipse (a label). However, I don't want to include this TextBlock into the Canvas because it will then rotate also. I want the text to remain horizontal.
Any idea how to solve this in an elegant way?
Something like this, should work for you
<TextBlock RenderTransform="{Binding RelativeSource={RelativeSource AncestorType=Canvas},
Path=RenderTransform.Inverse}"/>
Assign to text box transformation matrix an inverse of the transformation matrix of the Canvas.
Good question! And I'm going to guess, so please take this answer with a pinch of salt.
I believe you are trying to place text annotations next to ellipses on a rotated canvas, but these annotations need to remain horizontal. Two things you could try:
Firstly, given the XY point that you know of each ellipse from Canvas.GetTop/GetLeft, you could find its new rotated XY location by applying the RotateTransform to the ellipse location, using the formula U = M*V, where U is the output point, V is the input point (XY location of ellipse) and M is the Rotation Matrix.
Secondly, you could place a second canvas over the first (assuming they are both in a grid, the second canvas is at higher Z-index and is the same size as the underlying canvas). Call it an annotation layer. Your annotations (text labels) can appear at the new transformed locations and unrotated using this approach.
You'd have to do this in code of course, not Xaml, although you might find a binding friendly approach by creating a value converter on the TextBlock that bound to the source RotateTransform/Ellipse and did this operation for you.
Another approach would be to take the .Inverse of the RotateTransform and apply that to the textblocks, however you may still need to translate to get to the new location. Either way I think it demands some experimentation.
Best regards,
I am arranging quite freely a lot of elements on a Canvas layout, in fact the elements represent an interactive flow-chart. As transformations are applied, I need relative transformations on some of the elements.
Especially I require some elements being anchored or docked to their parent elements. I found different solutions, however I don't know if they solve my problem in the most elegant way.
Here is an example:
<Line X1="80" X2="800" Y1="730" Y2="730"/>
<Polygon Points="0,30 40,0 40,60" Canvas.Left="48" Canvas.Top="700"/>
The Polygon draws a triangle and I would like to let it dock on the left side of line. Which means, when translating the line to a new position or when scaling it down, the Polygon should move with it.
Is this possible?
put them in a canvas of their own, that way you can position the outer canvas absolutely and keep the inner stuff together.
like this:
<Canvas>
<Line X1="60" X2="820" Y1="60" Y2="760"> <!--some other line--> </Line>
<Canvas Canvas.Left="48" Canvas.Top="700">
<Polygon Points="0,30 40,0 40,60"/>
<Line X1="32" X2="752" Y1="30" Y2="30"/>
</Canvas>
</Canvas>
I'm creating a map in Silverlight that contains a bunch of rooms. All of the rooms are polygons, most of them are rectangles.
So what I'm doing is creating a Polygon using the x y point coordinates for the room. Something like
Polygon p = new Polygon
p.points = pointCollection;
MainCanvas.Chilren.Add(p);
This works great. Next, I want to put the name of each room inside these on the map. I figured I'd just add a textblock child to the polygon. Unfortunately, it seems that UIElements cannot contain child UIElements. I think this is dumb, but whatever.
Next plan: Create a new Silverlight user control containing the polygon and a text field. Center text field in polygon within user control. So I just create one of these UserControls "Room" set the property "RoomShape" to the polygon and add it to the canvas. The problem here is that The Usercontrol is always added at position (0,0) and the room shape is in the bottom right position. What I really wanted was for the control to take on the size and shape of the polygon I add to it so I can find the center and add the text in. Is this possible?
Also, let's say I add a polygon to my layout in a control:
LayoutRoot.Children.Add(poly);
Why do
poly.ActualHeight
poly.ActualWidth
poly.height
poly.width
all return 0 or NaN when I can clearly see them on the Canvas?
And why do
Canvas.GetTop(poly);
Canvas.GetLeft(poly);
both return 0 when I can clearly see they are not located at (0,0)?
Any other suggestions for centering text in a polygon is appreciated.
First of all, for sure you cannot add UIElements to Polygon. The Polygon is not a container so you can't add elements in it.
Second, if you want to set text in the middle of your polygon, why don't you wrap your polygon in a Grid. Then in the Grid, add your polygon, and your textblock like that :
<Grid x:Name="room1" Background="LightBlue" HorizontalAlignment="Center" VerticalAlignment="Center">
<Polygon Stroke="Black" Fill="Beige">
<Polygon.Points >
<Point X="0" Y="0" />
<Point X="20" Y="100" />
<Point X="100" Y="50" />
<Point X="0" Y="0" />
</Polygon.Points>
</Polygon>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Room Name</TextBlock>
</Grid>
Third, for the size always at 0, this is probably because you create/add the usercontrol at Load time. I know its sad but you will have to do the work in the UserControl_SizeChanged event of the container.