Estimate position and Width/Height of rendered UserControl - c#

I have a UserControl (boxes) that can have varying size based on the number of items in its ItemsControl.
Many such usercontrols are added to a Canvas programmatically.
I need to draw arrows interconnecting these usercontrols. What is the best way to get the origin coordinates of the control w.r.t the Canvas and the rendered Width/Height so that I can figure out the arrow start and endpoints.

Canvas provides the coordinates of each control via Canvas.Left and Canvas.Top attached properties - which you know if you positioned them yourself anyway. So the (slightly) harder part is getting the other coordinate, and for that you want to know the rendered height/width. ActualHeight and ActualWidth give you this, assuming the control has already been laid out:
double top = Canvas.GetTop(control)
double bottom = top + control.ActualHeight
double left = Canvas.GetLeft(control)
double right = left + control.ActualWidth
If you're doing this before the controls have had a chance to be rendered on the screen, you can first do control.UpdateLayout() (or control.Measure()) to ensure the layout system measures their size.

Related

Render an image and overlaid controls in exact pixels?

I have a background image that consists of an outline, with 10 rectangles placed specifically on the outline. I want to display that image on the screen, and overlay transparent buttons that match up to the rectangles on the image. When the button is pressed, the button's outline will turn to orange to indicate the rectangle has been selected.
I'm finding that simply setting the width and height of the buttons to match the pixel width and height of the rectangles in the image always renders a button much larger than the corresponding rectangle.
The image itself is 669 x 871px, and the rectangles are all 87 x 143px. Setting the Image's WidthRequest and HeightRequest in code or in XAML seems to render it at a different resolution than it actually is, thereby leading to the issue I described - if a button's size is set to 87 x 143 (again using HeightRequest and WidthRequest) it always renders larger than the rectangle.
Furthermore, if I contain the buttons in a StackLayout (as I often need to do) and specify a margin to push the layout downwards to align with the rectangles on the image, again the correct number of pixels never matches up with the distance the layout is moved.
I'm aware of how to overlay elements using a Grid with a single row and column and simply defining multiple layouts in the same space and triggering visibility in code. What I don't know how to do is display the elements in exactly the right pixel sizes that I need for the controls to always align with the image.
The diagram I'm working with:
Desired behaviour:
Actual behaviour:
I've worked out how to do this by using Grids within a Grid. By setting the Grid's row and column height and width to "*" (use all available space), defining a ContentView containing the diagram, then several smaller grids to lay over the top with their row height and column width matching the dimensions of the diagram image (i.e. if the image is 600 pixels wide, the nested Grid's ColumnDefinition's Width="600"),
I was then able to define the buttons inside a StackLayout, inside the nested Grids. I've tried this out across a few different resolutions and it seems to work just fine for my purposes.
I had to set Padding to 0, and used the Margin to vertically align the StackLayout as needed (i.e. for the first row of buttons at the top of the diagram, their StackLayout's Margin was "19,89,19,0" - 19 pixels from either side, 89 pixels from the top).

auto sliding of Scrollbars

In my Winforms application, i have a User Control which serves as a 'screen' to draw various 2D shapes.
i have set its 'AutoScroll' property to true, and scrollbars works fine when you zoom the screen( i.e. User control)
Now, when i select any shape ( like rectangle or circle etc) and move it so that it goes beyond visible part of screen, i want respective scroll bars to auto slide in order to keep that shape on the visible area of screen.
do i need to set any other property of scrollbar ??
I don't think it is possible to achieve that without creating your own method.
You can set your scrollbar positon with:
this.VerticalScroll.Value = Y;
Then you have to find out the position of your Rectangle via:
Rectangle.Location.Y;
So this should work for your vertical scrollbar:
this.VerticalScroll.Value = Rectangle.Location.Y;
horzontal:
this.HorizontalScroll.Value = Rectangle.Location.X;
Combined with a MouseDown-Event it will do the trick.
Take a look here at the MSDN documention on exactly what the AutoScroll property is and does. It simply will enable the container to have a virtual size that is larger than its visible boundaries. It doesn't actually do the scrolling for you.
If you want the control to "move" with the user as they drag a shape, you will have to capture that action on your own and manually scroll the control over. I'd suggest starting with the MouseDown and MouseMove events. You'll need some logic to figure out when scrolling is needed and how much to actually scroll.

Differences between Canvas Property and Margin

There are any differences between using SetValue with (for example) Canvas.LeftProperty and Margin Property?
To be more clear:
<Canvas>
<Rectangle x:Name="rect">
</Canvas>
Is
rect.SetValue(Canvas.LeftProperty, 10)
equivalent to
rect.Margin = new Thickness(10, 0, 0, 0)
or not?
The Margin property is used with every element to determine extra space around the object. It works in almost all types of layouting (StackPanel, Grid, ContentControls, etc.)
The Canvas.LeftProperty (as well as Top, Right, and Bottom) only apply to elements which are directly inside a Canvas. The Canvas will use these values to determine where an object should be. With Shape elements like a Path, the Canvas also looks at the location data of the Shape when determining the position.
Technically, the Canvas attached properties should accumulate with the inherent location data of a Shape (if any) as well as the Margin. You could use all 3 to modify the position. But usually you would try to keep it simple.
For example, a Rectangle should use its Width and Height properties as well as Canvas.Left and Canvas.Top. A Path would either just rely on its point data or it would offset it with Canvas.Left and Canvas.Top. You shouldn't use margin for elements inside a Canvas since you have better control without using it, but you technically could.
For an element directly in a Canvas, the only difference is in the means. The end result is exactly the same.
When the Rectangle is positioned, the layout engine will add together all the values that affect its position. For the X coordinate this includes Canvas.Left and Margin.Left.
So in one case it is adding 10 + 0, and in the other it is adding 0 + 10.
So go with whichever you prefer. Personally I prefer Canvas.Left in this situation as it seems to make more contextual sense.
It looks same but in first case canvas moves you rect right in 10 point. In second rect moves right in 10 point.
Use one of this way according to your purposes.
EDIT: If look more deeply in WPF code. In first case rect is moved in ArrangeOverride of Canvas in second case in ArrangeOverride of Rectangle.
Canvas.Left
Canvas.Right
Canvas.Top
Canvas.Bottom
are enforced only inside the canvas container, they will not alter the width and height of your the control they're being attached to. (left=10, right=10 will not stretch to give a margin of 10 on each side)
Margin.Left
Margin.Right
Margin.Top
Margin.Bottom
Is controlled by the object that it's been set on, it will work outside of a Canvas on a grid for example and will alter the width and height of any control to enforce it's value (IF the width and height are NaN).
You can use the Margin property inside your template of the object using a TemplateBinding or regular Binding as it is a DependencyProperty. This also adjusts the width and height of the object.
The Canvas properties are attached properties to the FrameworkElement and allow positioning of elements that might not have a Margin property. And they do not work if the object does not have a Canvas parent.

How to obtain visible borders of canvas placed on scrollviewer

I have canvas which is placed on scrollviewer in order to allow scrolling. Is it possible to get coordinates of visible part of canvas?
I was trying to calculate it that way
leftBorder = ScrollViewer1.HorizontalOffset;
rightBorder = ScrollViewer1.ViewportWidth - ScrollViewer1.HorizontalOffset;
topBorder = ScrollViewer1.VerticalOffset;
bottomBorder = ScrollViewer1.ViewportHeight - ScrollViewer1.VerticalOffset;
but it seems that it is not working.
The Horizontal and Vertical offset is the actual scroll value in that direction.
Besides that, if you want the size of the content without any scrollbars that might be visible.
You can search for the child named "PART_ScrollContentPresenter". This shows the actual content of the scrollviewer, and this content will be resized when the scrollbars needs more space.
Hope that helps.

WPF: How to find space available for Canvas?

I'm using WPF shapes to create Hexagons (for a game map) on a Canvas. After some playing around with ScrollViewer, I've decided to implement the scrolling and zoom of the map myself rather than using WPF functionality, just using WPF to get the events for mouse wheel, arrow keys etc. I'm placing the (Hex Map) Canvas as the last child inside a Dock Panel so it will get all the available remaining space. The Dock Panel will be set to be the content of the Main Window. But I want to find out how big the Canvas can be before I put any Children on the Canvas so that I can centre the screen over the Hex I want and only add the Shapes (Hexs) that can actually be seen. When zoomed out, a long way I will remove Polygons altogether and use another method of rendering and when zoomed in a long way I will add more details.
Is there any neat way of getting the available space? The only way that I can think of that will hopefully work is to get the current dimensions of the windows and subtract the dimensions of the outer elements of the Dock Panel, but that feels rather messy.
You may use the ActualWidth and ActualHeight properties of Canvas to determine size available to it. Be sure that HorizontalAlignment and VerticalAlignment are set to Stretch.

Categories