WPF align Adorner with GuidelineSet - c#

I want to create a simple Adorner that marks a selected element with a bounding box. I want it to be sharp and be exactly one pixel outside the target content. I found some useful code, but it does not work exactly as I wanted it to.
The OnRender method of my SelectAdorner is:
Rect adornedElementRect = new Rect(AdornedElement.DesiredSize);
SolidColorBrush renderBrush = new SolidColorBrush(Colors.Transparent);
Pen renderPen = new Pen(new SolidColorBrush(Colors.LightBlue), 1);
double halfPenWidth = renderPen.Thickness / 2;
// Create a guidelines set
GuidelineSet guidelines = new GuidelineSet();
guidelines.GuidelinesX.Add(adornedElementRect.Left + halfPenWidth);
guidelines.GuidelinesX.Add(adornedElementRect.Right + halfPenWidth);
guidelines.GuidelinesY.Add(adornedElementRect.Top + halfPenWidth);
guidelines.GuidelinesY.Add(adornedElementRect.Bottom + halfPenWidth);
drawingContext.PushGuidelineSet(guidelines);
drawingContext.DrawRectangle(renderBrush, renderPen, adornedElementRect);
The problem is that the bounding box is not aligned properly around the content (see second and third item). I want the result to be the one from the bottom item in the picture below.
Any ideas how can I achieve what I want?
Also, it would be nice to have the Adorner working in the same way with renderPen.Thickness greater than 1, in case I need that.
Thanks for the help.

The following code needs to be added before creating the GuidelineSet:
adornedElementRect = new Rect(
adornedElementRect.Left - halfPenWidth,
adornedElementRect.Top - halfPenWidth,
adornedElementRect.Width + renderPen.Thickness,
adornedElementRect.Height + renderPen.Thickness
);
The problem comes from the fact that adornedElementRect overlaps AdornedElement when the pen thickness is taken into consideration. The value of AdornedElement.DesiredSize corresponds to the outer edge of the border. That means that the Adorner is drawn half over the border and half outside the border. The GuidelineSet then aligns the right side of vertical edges and the bottom side of horizontal edges to pixel boundaries. This makes the drawing look sharp, however the edges are moved either to the inside, either to the outside of the AdornedElement. This is what creates the artifact.
When adding the specified changes to adornedElementRect, then it is drawn outside and right next to the AdornedElement. Now, the GuidelineSet aligns the Adorner similarly with the Border control. See this link for more information about how WPF draws the content on the screen.

Related

Getting Bottom Left location of scroll able Screen

Description Created a form in Winform C# app.
Added a panel (PBack) with dock type fill. (Scrollable)
Then added a picturebox(pbDraw) in panel(PBack) that height depends upon image size.
I want to add a control on the bottom left of the current screen view. (assume client scrolled down)
What i tried
Rectangle rect = Screen.GetWorkingArea(pbDraw);
ctrl.Top = rect.Top + rect.Height;
ctrl.Top = Screen.PrimaryScreen.WorkingArea.Top + Screen.PrimaryScreen.WorkingArea.Height;
ctrl.Top = Screen.FromControl(pbDraw).WorkingArea.Top+ Screen.FromControl(pbDraw).WorkingArea.Height;
Issue control is adding at top of pbDraw (0,0) and not on current screen bounds top.
As far as I understand, Control.Top takes the scrolled view into account all by itself.
Gets or sets the distance, in pixels, between the top edge of the control and the top edge of its container's client area.
So, you should be able to assign the coordinates relevant to the pBack and its client view:
ctrl.Top = pBack.Height - ctrl.Height;
Assuming that ctrl is a child of pBack this code should place it at the bottom of the current (scrolled) part of pBack
UPDATE:
As you said in the comments, ctrl is actually a child of pbDraw. In this case, you'll need to take the scrolling into account. For that, you can use Panel.VerticalScroll:
//scroll position + panel height - control height
ctrl.Top = pBack.VerticalScroll.Value + pBack.Height - ctrl.Height;
If I were you, I'd add ctrl to the panel, on top of the picture box. This will make it easier to calculate offsets relative to the panel.

How to make graphics (Shape, animation) in c#

How to make a circle with text inside ?? then move it from one location to another, and then access it later (to delete it).
I want to make something like this
Your question is really very broad and you got a few nice links you should study to learn all about GDI+ drawing.
But if taken literally there is a slightly exotic alternative which puts the burdon of most chores onto the Chart control from DataVisualization.Charting.
You can create EllipseAnnotations and add them to a Chart control.
Disable the Axes and clear the Legends and then use code like this to add a moveable circle wit thext inside:
EllipseAnnotation ea = new EllipseAnnotation();
ea.X = 11; // put at..
ea.Y = 11; // 11% of the chart's area
ea.AllowMoving = true;
ea.BackColor = Color.BlanchedAlmond;
ea.Text = (chart1.Annotations.Count + 1) + "";
chart1.Annotations.Add(ea);
Note that there are quite a few annotation types available. which allow you to add Rectangles, Images, Polygons, Lines and pure Text.
And another pro is that saving or loading the graphics takes only one line each, as you can serialize a Chart out of the box!
:-)
GraphX for .NET is an advanced open-source graph layout and visualization library that supports different layout algorithms and provides many means for visual customizations It is capable of rendering large amount of vertices
https://github.com/panthernet/GraphX
To draw shapes follow here.Also you need a complete tut,you can follow here
Some insight is here:
To draw a simple shape at design time Drag the OvalShape or
RectangleShape control from the Visual Basic PowerPacks tab (to
install, see Visual Basic Power Packs Controls)in the Toolbox to a
form or container control.
Drag the sizing and move handles to size and position the shape. You
can also size and position the shape by changing the Size and Position
properties in the Properties window To create a rectangle with rounded
corners, select the CornerRadius property in the Properties window
and set it to a value that is greater than 0. In the Properties
window, optionally set additional properties to change the appearance
of the shape. To draw a simple shape at run time On the Project
menu, click Add Reference. In the Add Reference dialog box, select
Microsoft.VisualBasic.PowerPacks.VS, and then click OK. In the Code
Editor, add an Imports or using statement at the top of the module:
using Microsoft.VisualBasic.PowerPacks; Add the following code in an Event procedure:
ShapeContainer canvas = new ShapeContainer();
// To draw an oval, substitute
// OvalShape for RectangleShape.
RectangleShape theShape = new RectangleShape();
// Set the form as the parent of the ShapeContainer.
canvas.Parent = this;
// Set the ShapeContainer as the parent of the Shape.
theShape.Parent = canvas;
// Set the size of the shape.
theShape.Size = new System.Drawing.Size(200, 300);
// Set the location of the shape.
theShape.Location = new System.Drawing.Point(100, 100);
// To draw a rounded rectangle, add the following code:
theShape.CornerRadius = 12;
Customizing Shapes When you use the default settings, the OvalShape and RectangleShape controls are
displayed with a solid black border that is one pixel wide and a
transparent background. You can change the width, style, and color of
the border by setting properties. Additional properties enable you to
change the background of a shape to a solid color, a pattern, a
gradient fill, or an image. Before you change the background of a
shape, you should know how several of the properties interact. The
BackColor property setting has no effect unless the BackStyle property
is set to Opaque. If the FillStyle property is set to Solid, the
FillColor overrides the BackColor. If the FillStyle property is set to
a pattern value such as Horizontal or Vertical, the pattern will be
displayed in the FillColor. The background will be displayed in the
BackColor, provided that the BackStyle property is set to Opaque. In
order to display a gradient fill, the FillStyle property must be set
to Solid and the FillGradientStyle property must be set to a value
other than None. Setting the BackgroundImage property to an image
overrides all other background settings.
This SO link I found is also nice here

WPF: Get the real 'logical' position of Child within its parent Panel

Another problem in my WPF questions series :)
I'm creating custom Decorator that will be used to decorate Panels (or it can be Behavior, no difference).
That Decorator deals with elements that are in that Panel (Children property of the Panel). It attaches some RenderTransforms to those elements. Now I need a position of some element ('child' of the Panel) relative to the Panel itself. In other words I need a position if some child element in the Panels coordinate space. in just another words I want the offset that was specified by the ArrangeOverride method of the Panel when calling Arrange method on Children.
That seem to be easy. But I cannot find the way to always get right coordinates.
This code
VisualTreeHelper.GetOffset(child)
does not work when the panel is inside ScrollView - it takes topmost, leftmost visible corner of the Panel as an origin of coordinate space - not the real topmost and leftmost corner of the Panel.
The code
Point position = child.TransformToAncestor(panel).Transform(new Point(0,0));
will not work when some render transforms are already active on the child element of the Panel. It will return the position of transformed image(by the render transfrom) of child element. The render position.
The same problem is with this aproach:
Point panelPosition = panel.PointToScreen(new Point(0, 0));
Point childPosition = child.PointToScreen(new Point(0, 0));
Point position = new Point(childPosition.X - panelPosition.X, childsPosition.Y - panelPosition.Y);
So this i what I have tryied but it did not work. I have 2 similar questions on this topic that were tying to simplify the problem, so I got some of suggestions above. Now I introduced the problem in its full complexity, I hope to get the right advice.
If something is unclear please leave the comment.
Thank you
You can "undo" the RenderTransform before calling TransformToAncestor:
Point origin = child.RenderTransform.Inverse.Transform(new Point(0, 0));
Point position = child.TransformToAncestor(listBox).Transform(origin);

Getting the top left coordinates of a WPF UIElement

I am working on extending the Microsoft resize Adorner example and need to be able to reposition the element after say the bottom left drag handle has been dragged.
So if I have a textbox of say 150 wide, 35 high postitioned on my form, and the bottom left drag handle changes the width to 200 wide, the right hand of the text box remains unchanged but the left hand edge moves to the left.
So I need to know the top left coordinates of the UIElement. I have tried Canvas.GetLeft and Canvas.GetTop but they return NaN which is confusing.
I just tried VisualTreeHelper.GetOffset which does return an offset but when you try and use it in the arrange method of the element it disappears, presumably as the values in the offset are too high.
In the days before Wpf the coordinate system was quite simple, wpf has overcomplicated things I think.
And if someone just wants the control's screen coordinates:
Point targetLoc = targetCtrl.PointToScreen(new Point(0, 0));
(this doesn't match the thread's description, but it does match the title. Figured it might help people coming in off search results)
You can transform coordinates of the UIElement to its parent. In your case it's a form. Here is an example of a method that returns coordinates of a visual:
private Point GetPosition(Visual element) {
var positionTransform = element.TransformToAncestor(MyForm);
var areaPosition = positionTransform.Transform(new Point(0, 0));
return areaPosition;
}

Draw a Grid over a Picturebox in c#

I am trying to make a tool in c# which allows the user to put a grid on the screen on a picturebox. At the moment i don't know how to do this, so when a button is clicked, the picturebox comes up with a grid. It needs to be a grid which is spaced out enough that users can find out locations of objects on the picture in the picturebox. Help with what code i can use to do this would be very helpful as i was going to use ControlPaint.DrawGrid but not sure of the values i need to put in it to get my desired effect?
Thanks
Form the Documentation od controlpaint.Drawgrid,
I suppose you need to decide on the cell size in x- amd y-direction and pass this as a size parameter to Drawgrid:
public static void DrawGrid(
Graphics graphics,
Rectangle area,
Size pixelsBetweenDots,
Color backColor
)
for example, a 100*200 pixels square grid would be generated by
setting graphcis to the context you want to draw upon,
Setting area to the top left right and bottom parameters of your image
setting size.x to 100 and size.y to 200
setting color to any color you like.
Update
Something like this should do.
Rectangle myRect = new System.drawings.Rectangle();
myRect.Location := new System.Drawing.Point(0,0);
myRect.Height = 50;
myRect.Width = 50;
Drawgrid(FromImage(yourImage), mygrid , yourImage.Size, System.Drawing.Color.Black);
Disclaimer: i don't develope in c#, so above code is not tested for anything. I just picked stuff from the documentation (msdn).

Categories