Draw method of subclassed UIView (child of another subclassed UIView) not called - c#

I have a subclassed parent UIView object which should add another subclassed UIView. This is the UIView I want to add and where the Draw method is not called:
public class Circle : UIView
{
private UIColor color;
public Circle ()
{
this.color = UIColor.Black;
this.BackgroundColor = UIColor.Clear;
}
public Circle (UIColor color)
{
this.color = color;
this.BackgroundColor = UIColor.Clear;
}
public override void Draw (CGRect rect)
{
base.Draw (rect);
// Get the context
CGContext context = UIGraphics.GetCurrentContext ();
context.AddEllipseInRect (rect);
context.SetFillColor (color.CGColor);
context.FillPath ();
}
}
This is how I'm adding the circle:
Circle circle = new Circle (UIColor.Red);
circle.TranslatesAutoresizingMaskIntoConstraints = false;
AddSubview (circle);
AddConstraint(NSLayoutConstraint.Create(circle, NSLayoutAttribute.Left, NSLayoutRelation.Equal, line, NSLayoutAttribute.Left, 1, 10));
AddConstraint(NSLayoutConstraint.Create(circle, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, line, NSLayoutAttribute.CenterY, 1, 0));
AddConstraint(NSLayoutConstraint.Create(circle, NSLayoutAttribute.Height, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1, 6));
AddConstraint(NSLayoutConstraint.Create(circle, NSLayoutAttribute.Width, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1, 6));
This code above is again in the Draw method of the parent. The objects in the parent are drawn fine except the circle and even if I use the below code as circle it is shown correctly. So the constraints are ok.
UIView circle = new UIView() { BackgroundColor = UIColor.Red };
What I'm doing wrong? Can't I override both Draw methods (in the subclassed parent and in the subclassed circle)?
PS: I have to mention that the circle should overlaps a line. But the Draw is never called so it seems that it doesn't get a frame.

Are you aware that you are instantiating an UIView and not a Circle in this code snipped ?
UIView circle = new UIView() { BackgroundColor = UIColor.Red };
Also you shouldn't add subview in the draw method, because it will be called multiple time, in fact you should only override the Draw method with you are doing a custom draw (it´s the case of the Circle view, but not the case of the parent view).
From apple documentations:
View drawing occurs on an as-needed basis. When a view is first shown,
or when all or part of it becomes visible due to layout changes, the
system asks the view to draw its contents. For views that contain
custom content using UIKit or Core Graphics, the system calls the
view’s drawRect: method
So can you post the code snipe where you actually add the parent view? And the code of the parent view, you might have override a method and are not calling the base class method (like setNeedsDisplay or something similar) OR you are not adding the view.

Related

Check to avoid adding multiple items to canvas in WPF/C#

Shape shape = sm.maakEllips();
if (!canvas.Children.Contains(shape))
{
cm.Draw(shape, canvas, locatie);
}
public void Draw(Shape vorm, Canvas canvas, Point locatie)
{
if (vorm.Height <= canvas.Height && vorm.Width <= canvas.Width)
{
Canvas.SetTop(vorm, locatie.Y);
Canvas.SetLeft(vorm, locatie.X);
canvas.Children.Add(vorm);
}
}
So I add a shape to a canvas in the Draw(). Then when I check on this in the upper if clause, I'm still able to add the same shape to the same canvas multiple times.
I don't get it, what am I doing wrong?
EDIT:
Shape shape = sm.makeShape(Convert.ToByte(textboxR.Text), Convert.ToByte(textboxG.Text), Convert.ToByte(textboxB.Text), Convert.ToInt32(textboxHoogte.Text), Convert.ToInt32(textboxBreedte.Text));
foreach (Shape existingShape in canvas.Children.OfType<Shape>())
{
if (existingShape.Width != shape.Width && existingShape.Height != shape.Height
&& existingShape.Fill != shape.Fill)
{
cm.Draw(shape, canvas, locatie);
}
}
I tried this and now I'm not even able to add a shape to the canvas at all.
I don't see what I'm doing wrong at all.
Your Draw() method add vorm of type Shape to the canvas specified in canvas. And I assume your sm.maakEllips() returns an ellipse.
Therefore, when you run the following code:
Shape shape = sm.maakEllips();
if (!canvas.Children.Contains(shape))
{
cm.Draw(shape, canvas, locatie);
}
You will go inside the if statement only if the canvas contains that exact shape object you created in the line above, using sm.maakEllips() method. It cannot be any shape that has the same properties of the shape object above. Because, every time you create a new object, even with the exact same properties including its name, they are still two distinct objects in .NET world.
To illustrate the point see the code sample below.
Your unchanged Draw() method:
public void Draw(Shape vorm, Canvas canvas, Point locatie)
{
if (vorm.Height <= canvas.Height && vorm.Width <= canvas.Width)
{
Canvas.SetTop(vorm, locatie.Y);
Canvas.SetLeft(vorm, locatie.X);
canvas.Children.Add(vorm);
}
}
A makeEllipse() method that creates an ellipse of width and height of 100 and 80 respectively, and assigns the name passed in the parameter.
public Shape makeEllipse(string name)
{
Shape sh = new Ellipse
{
Name = name,
Width = 100,
Height = 80,
};
return sh;
}
Now see the following code, executed at the click of a button.
private void btnGO_Click(object sender, RoutedEventArgs e)
{
// Creates an ellipse with name "Shape1", and assigns to sh1.
Shape sh1 = makeEllipse("Shape1");
// Adds the said sh1 to the canvas using `Draw()` method.
Draw(sh1, myCanvas, new Point(5, 5));
// See if sh1 exists as a child of `myCanvas`.
// Since sh1 is now a child of canvas, code does NOT go inside the if-clause.
if (!myCanvas.Children.Contains(sh1))
{
Draw(sh1, myCanvas, new Point(5, 5));
}
// Creates an ellipse with the same name "Shape1", and assigns to sh2.
Shape sh2 = makeEllipse("Shape1");
// It is NOT added to the canvas using `Draw()` method.
// Now, here, code DOES go inside the if-clause, because the said object does not exist as a child of `myCanvas`.
if (!myCanvas.Children.Contains(sh2))
{
Draw(sh2, myCanvas, new Point(5, 5));
}
}
Comments above should be good enough, but to explain again,
When you create sh1 and adds it to myCanvas using Draw() method, it becomes a child element of myCanvas.Children.
Then, when you check if it is a child using if (!myCanvas.Children.Contains(sh1)), since it IS a child element by that time, condition becomes false and we do not go inside the if clause.
Next, we create sh2, which has the exact same dimensions and the name as sh1. However, and this is the key, .NET treats it as a different object even though it has the same properties as the previous object. Reason being, whenever we use the new keyword, .NET creates an actual new object.
Afterwards, we DON'T add it to the canvas using Draw() method.
Now, at the second if when we check if myCanvas contains the object, it finds that sh2 is NOT a child of myCanvas, so it goes inside the if clause.
maakEllips() always creates a new Shape. If you want to compare this one against other Shape elements in the Canvas, you need to iterate through these. The following code compares the height, width and position and adds the new Shape to the Canvas if any of them differ:
Shape shape = sm.maakEllips();
foreach (Shape existingShape in canvas.Children.OfType<Shape>())
{
if(existingShape.Width != canvas.Width || existingShape.Height != canvas.Height
|| Canvas.GetLeft(existingShape) != Canvas.GetLeft(shape)
|| Canvas.GetTop(existingShape) != Canvas.GetTop(shape))
{
cm.Draw(shape, canvas, locatie);
}
}

How to draw a Graphics ? - xamarin

I am trying to create a graphic in the middle of my layout, which can be restricted in a Linearlayout. Example => Print App This is my application and the graphic has to be in the white area written "Diagram".
The graphic only needs lines and bezier curves.
in fact I do not know if it has a specific object where I can draw.
with lines (x1, y1, x2, y2).
public class MyView : View
{
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Paint green = new Paint {
AntiAlias = true,
Color = Color.Rgb(0x99, 0xcc, 0),
};
green.SetStyle(Paint.Style.FillAndStroke);
Paint red = new Paint {
AntiAlias = true,
Color = Color.Rgb(0xff, 0x44, 0x44)
};
red.SetStyle(Paint.Style.FillAndStroke);
float middle = canvas.Width * 0.25f;
canvas.DrawPaint(red);
canvas.DrawRect(0, 0, middle, canvas.Height, green);
}
}
How do I draw this in a specific place in my layout?
How do I draw this in a specific place in my layout?
After you've complete your draw view MyView, you can place it in your layout in xml for example like this:
<YOURNAMESPACE.MyView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/myview" />
In this case, it can be used like normal UI components in the layout, means you can use Margin, Gravity and such layout parameters to certain the location where it should be placed.
If you want to implement it in the code behind, you can use LayoutParams. Specific layout code depends on which parent view are you using.
Basically it means, you can draw the view use Paint in the subclass of View and place it like a UI components in your layout.

WPF InkCanvas: Draw line with DynamicRenderer

I'm writing basic graphic editor in WPF using InkCanvas. I've made some custom shapes (inherited from Stroke). When I draw line wen on InkCanvas, I take first and last point and make a line. That works fine, but now I don't like the default "pen stroke" so i decided to rewrite DynamicRenderer to render line in real time.
Problem is, that DynamicRenderer draws line from origin point to every point of stroke and i obviously don't want that, becouse it makes "fan" insted of line.
There is my custom code and I'm looking for solution to draw line from origin point to last point only, if it is possible.
class LineRenderer : DynamicRenderer
{
private Point firstPoint;
private Pen pen = new Pen(new SolidColorBrush(Colors.Gray),1);
public LineRenderer()
{
firstPoint = new Point(double.PositiveInfinity, double.PositiveInfinity);
}
protected override void OnStylusDown(RawStylusInput rawStylusInput)
{
firstPoint = new Point(rawStylusInput.GetStylusPoints().First().ToPoint().X, rawStylusInput.GetStylusPoints().First().ToPoint().Y);
base.OnStylusDown(rawStylusInput);
}
protected override void OnDraw(DrawingContext drawingContext,
StylusPointCollection stylusPoints,
Geometry geometry, Brush fillBrush)
{
drawingContext.DrawLine(pen, firstPoint, stylusPoints.First().ToPoint());
}
protected override void OnStylusUp(RawStylusInput rawStylusInput)
{
firstPoint = new Point(double.PositiveInfinity, double.PositiveInfinity);
base.OnStylusUp(rawStylusInput);
}
}
This is very much late. In order to avoid the "fan" while the stroke is being drawn, try this:
protected override void OnDraw(DrawingContext drawingContext,
StylusPointCollection stylusPoints,
Geometry geometry, Brush fillBrush)
{
if (!_isManipulating)
{
_isManipulating = true;
StylusDevice currentStylus = Stylus.CurrentStylusDevice;
this.Reset(currentStylus, stylusPoints);
}
_isManipulating = false;
var pen = new Pen(brush, 2);
drawingContext.DrawLine(pen, startPoint,stylusPoints.First().ToPoint());
}
protected override void OnStylusDown(RawStylusInput rawStylusInput)
{
StylusPointCollection y = rawStylusInput.GetStylusPoints();
startPoint = (Point)y.First();
// Allocate memory to store the previous point to draw from.
prevPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);
base.OnStylusDown(rawStylusInput);
}
The trick here is to use DynamicRenderer.Reset, which:
Clears rendering on the current stroke and redraws it.
The redraw will reenter the OnDraw method, so _isManipulating provides a simple flag to stop looping.
what i think in your ondraw function your first point is fixed so it always take it as origin so you can try this for continous drawing.
protected override void OnDraw(DrawingContext drawingContext,
StylusPointCollection stylusPoints,
Geometry geometry, Brush fillBrush)
{
drawingContext.DrawLine(pen, firstPoint, stylusPoints.First().ToPoint());
firstPoint = stylusPoints.First().ToPoint();
}
it will update your firstpoint if you get same point exception put a check on it ( or you can define anew point name as previous point and initially make it equal to first point and keep it updating in your ondraw method as i did with first point..
and if you want direct line from first point to last point you can called you on draw method after getting last point from onstylusup method ..hope this might help you..

Highlight the Rectangular Area while Dragging it

I am creating a image viewer sort of application. I am on Windows and using .Net
In my app, I am trying to highlight a Particular area while dragging.
I have created a Rectangle.
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Point ptOld = new Point(0, 0);
Pen rectPen = new Pen(Brushes.White, 3);
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.DrawRectangle(rectPen, areaRect);
}
Now I am dragging this rectangular area along with my mouse movements.
protected override void OnMouseMove(MouseEventArgs e)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy);
MoveRect(ptNew);
ptOld = ptNew;
}
Here I am trying to move this rect along with my mouse
void MoveRect(Point point)
{
Graphics grfxClient = CreateGraphics();
Rectangle tempRectangle = new Rectangle(areaRect.Left, areaRect.Top, areaRect.Width, areaRect.Height);
grfxClient.DrawRectangle(rectPen, tempRectangle);
this.Invalidate();
grfxClient.Dispose();
}
My Code till this point is working fine.
Now I would like to darken the INVERSE drag Area (The area which is outside the drag region), I mean the area which is within this Rectangle should gets highlighted while dragging.
Any idea how to proceed.
Thanks.
-Pankaj
I suppose you can do it by creating a Region object that covers the outside of the rectangle and fill it with a semi-transparent SolidBrush to make it look darkened.
You also don't have to create a graphics and draw in OnMouseMove event, but just shift the rectangle and invalidate the surface of the control you are drawing on.
The code I used looks more or less like this:
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Point ptOld = new Point(0, 0);
Pen rectPen = new Pen(Brushes.White, 3);
//A new field with a semi-transparent brush to paint the outside of the rectangle
Brush dimmingBrush = new SolidBrush(Color.FromArgb(128, 0, 0, 0));
protected override void OnPaint(PaintEventArgs e)
{
Region outsideRegion = new System.Drawing.Region(e.ClipRectangle);
outsideRegion.Exclude(areaRect);
Graphics dcPaint = e.Graphics;
dcPaint.FillRegion(dimmingBrush, outsideRegion);
dcPaint.DrawRectangle(rectPen, areaRect);
}
protected override void OnMouseMove(MouseEventArgs e)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy);
ptOld = ptNew;
this.Invalidate();
}
The method named MoveRect is not needed.
It now seems to work as you wanted it to.
Suggestions
I also have some suggestions. You don't have to use them, maybe they will be helpful for you.
You haven't written what kind of control you are using to draw on (or overriding Form methods and painting directly on it), but I suggest you to use a PictureBox control, create a custom control derived from it and override its events. This should make the painting process smooth and prevent flickering. To do it this way:
Create a new user control by selecting Add and User Control... and name a new control i.e. MyPictureBox
change the parent class of the control, so it should now contain the line:
public partial class MyPictureBox : PictureBox
open file MyPictureBox.Designer.cs and comment out these lines:
//this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
//this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
copy the code I posted in this answer and add line base.OnPaint(e); and the beginning of OnPaint method
compile the project
now you should be able to open designer of your main form, drag MyPictureBox control from the toolbox and use it without additional code needed
You also might consider changing the behaviour of the highlighted area, so mouse cursor was in the center of it. I suppose it would be more intuitive to the user.
If you have any issues with the code, just write it in the comments and I'll try to help :).

WinForms system-renderer Toolstrip item hover effect on button controls

If you take a look at the attached image, is there a way to get the drawing logic for this hover effect from the system renderer of the standard WinForms toolstrip ?
http://imageshack.us/photo/my-images/10/toolstriphovereffect.jpg/
EDIT: Anyway, I've manually implemented this with images, but if anyone comes here with a solution, please post.
Maybe this code helps. It draws red circle with black border around toolstripbutton when mouse is over it.
Set your toolstrip properties:
//Set render mode to professional
myToolStrip.RenderMode = ToolStripRenderMode.Professional;
//Assign new instance of your custom renderer
myToolStrip.Renderer = new MyCustomRenderer();
Custom renderer class:
public class MyCustomRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e)
{
if (!e.Item.Selected)
base.OnRenderButtonBackground(e);
else
{
Rectangle rectangle = new Rectangle(0, 0, e.Item.Size.Width - 1, e.Item.Size.Height - 1);
//Draw red circle
e.Graphics.FillEllipse(Brushes.Red, rectangle);
//Draw black border
e.Graphics.DrawEllipse(Pens.Black, rectangle);
}
}
}

Categories

Resources