drawing graphics to form in c# - c#

i want to call a function from an external script which draws a shape.
the form currently opens up blank with no shape on it and no error.
below is empty form that calls a function from a different file
public partial class Form1 : Form
{
other_fff functions1 = new other_fff();
public Form1()
{
InitializeComponent();
functions1.draw_circle(this);
}
}
below is where i get my draw function
class other_fff
{
public void draw_circle(Form1 the_form)
{
Pen Pen1 = new Pen(Color.Blue, 9);
Graphics g = the_form.CreateGraphics();
g.DrawEllipse(Pen1, 50, 50, 10, 5); // does not work
}
}
how can i make this draw

You could add a event handler to the paint-event like this:
public static void DrawCircle(Form form)
{
form.Paint += OnPaint;
void OnPaint(object sender, PaintEventArgs e)
{
using (var Pen1 = new Pen(Color.Blue, 9))
{
e.Graphics.DrawEllipse(Pen1, 50, 50, 10, 5);
}
}
}
There are a number of downsides:
There is no possibility to remove circles unless you add some method of un registering the event.
It will be difficult to manage order of paint-calls, since it will depend on the order the eventhandlers are added.
It is a bit odd that an external part is drawing on a form. In general, forms and controls should manage their own painting if needed.
You might want to inform the form that it should draw an ellipse, and let the form do the drawing instead.

Related

Accessing Graphics from Different Methods?

I'm trying to access the variable "g" from a different method, it does not throw an error but doesn't write to the screen. Any way to use the graphics class from different methods? Paint Method:
public static Graphics g;
private void paintClass(object sender, PaintEventArgs e)
{
e.Graphics = g;
}
Method Im Trying To Access "g" From:
public void drawline(){
g.DrawLine(new Pen(Color.AliceBlue), 10, 10, 20, 20);
}
Note These Code Snippets Are from The Same Class
If this isn't possible, I am just trying to figure out a way to paint to the screen from a method that isn't the paint method.
Instead of storing it, pass it directly from your event:
private void paintClass(object sender, PaintEventArgs e)
{
drawline(e.Graphics);
}
public void drawline(Graphics g){
g.DrawLine(new Pen(Color.AliceBlue), 10, 10, 20, 20);
}
This approach would make more sense if the "draw" method was part of a different class. For example, if you had a collection of custom class instances and you pass the graphics to each one so they can each draw themselves onto a surface (with the graphics provided from the paint event of that surface).
I am guessing you could obtain what you want by using CreateGraphics().
var g = myButton.CreateGraphics();
g.DrawLine(Pens.Red, 0, 0, 20, 20);
Replace myButton with the Control or Form you want to draw on.
This may turn out to not be the right approach though. The drawing will disappear e.g. when the form is minimized, maximized, gets covered by another form etc.
The recommended pattern is to use the Paint event - see https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.paint?view=windowsdesktop-6
One thing you could try is that every time you call something like the drawline() method that you mention in your post, add this action to a List. Then, when the List changes, have your paint class fire a Refresh event that the containing control can use to ensure the canvas will be invalidated. When the control calls Refresh() on itself, the OnPaint override is called and the items in the list are iterated and drawn (or in some cases redrawn) in a legitimate Graphics context that doesn't rely on the temporary fix of calling CreateGraphics ad hoc. You could then make these calls from your main form:
Diag Button - Diagonal line adding 25 to offset each time.
private void buttonDiag_Click(object sender, EventArgs e)
{
_paintClass.DrawDiagonal(GetNextTestColor(), offsetX: 25 * _testCountDiag++);
}
Clicking the button 3x executes the Drawline with a different random known color each time.
Line Button - Draw a line between two points.
private void buttonLine_Click(object sender, EventArgs e)
{
var offsetY = 15 * _testCountLine++;
_paintClass.Drawline(
GetNextTestColor(),
new PointF(0, 100 + offsetY),
new PointF(ClientRectangle.Width, 100 + offsetY));
}
Under the hood
The PaintClass inherits List<PaintClassContext> where:
class PaintClassContext
{
public PaintOp PaintOp { get; set; }
public Color Color { get; set; }
public PointF Start { get; set; }
public PointF End { get; set; }
public float OffsetX { get; set; }
public float OffsetY { get; set; }
}
enum PaintOp{ DrawLine, Clear, DrawDiagonal}
Example: Implementation of DrawDiagonal
Add a new context to the current list.
public void DrawDiagonal(Color color, float offsetX = 0, float offsetY = 0) =>
Add(new PaintClassContext
{
PaintOp = PaintOp.DrawDiagonal,
Color = color,
OffsetX = offsetX,
OffsetY = offsetY,
});
Every time a new action is added, the Refresh event is fired and this repaints the canvas.
public new void Add(PaintClassContext context)
{
base.Add(context);
_modified = true;
Refresh?.Invoke(this, EventArgs.Empty);
}
The MainForm will be subscribed to the PaintClass.Refresh event and calls Refresh().
_paintClass.Refresh += (sender, e) =>
{
// Causes the control to repaint.
Refresh();
Text = CurrentTestColor.ToString();
};
This causes the control to redraw, where the PaintAll will iterate its current items and paint them in a legitimate graphics context rather than trying to "finesse" one.
PaintClass _paintClass = new PaintClass();
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
_paintClass.PaintAll(e.Graphics, always: false);
}

Visual Studio C# pictureBox - drawing in From constructor doesn't show up

I have the problem that I want to draw on a pictureBox with an image loaded before the user gets to see it. The drawing in principle works, but if I do it in the constructor it doesn't show up. It does work if I call the same function with a button.
public Form1()
{
InitializeComponent();
draw();
}
public void draw()
{
pictureBox1.Refresh();
Graphics g = pictureBox1.CreateGraphics();
Rectangle rect = new Rectangle(new Point(20, 20), new Size(40, 40));
rect.Offset(8, 8);
g.DrawEllipse(new Pen(Brushes.Black, 2), rect);
g.Dispose();
}
private void button1_Click(object sender, EventArgs e)
{
draw();
}
I would have assumed that the circle would show up when the Form loads but it only does when I press the button. The code itself gets executed, as tested with a Message box.
The control paints itself in its Paint event, so as soon as it's shown on screen (ie, after your code), it will paint itself in dull gray as normal.
If you want to custom paint a control, any control, you either override its OnPaint function or subscribe to its exposed Paint event.
A hint should be the fact that you had to create your own Graphics object, as opposed to using the object constructed during normal painting operations, that also includes proper clipping handling.

WinForms - How do I run a function with PaintEventArgs when the form is loaded?

I'm trying to understand the graphics, and in the Graphics.FromImage documentation, it has this as an example:
private void FromImageImage(PaintEventArgs e)
{
// Create image.
Image imageFile = Image.FromFile("SampImag.jpg");
// Create graphics object for alteration.
Graphics newGraphics = Graphics.FromImage(imageFile);
// Alter image.
newGraphics.FillRectangle(new SolidBrush(Color.Black), 100, 50, 100, 100);
// Draw image to screen.
e.Graphics.DrawImage(imageFile, new PointF(0.0F, 0.0F));
// Dispose of graphics object.
newGraphics.Dispose();
}
I'm new to C# and Windows Forms and am struggling to understand how this all fits together. How is this code run, say when the form first loads or when a button is pressed?
Maybe this will help. I have an example of both drawing on Paint events but also drawing on top of an existing Image. I created a form with two picture boxes. One for each case. pictureBox1 has an event handler for the .Paint event, while pictureBox2 is only drawn when a button is pressed.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
pictureBox1.BackColor=Color.Black;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// The code below will draw on the surface of pictureBox1
// It gets triggered automatically by Windows, or by
// calling .Invalidate() or .Refresh() on the picture box.
DrawGraphics(e.Graphics, pictureBox1.ClientRectangle);
}
private void toolStripButton1_Click(object sender, EventArgs e)
{
// The code below will draw on an existing image shown in pictureBox2
var img = new Bitmap(pictureBox2.Image);
var g = Graphics.FromImage(img);
DrawGraphics(g, pictureBox2.ClientRectangle);
pictureBox2.Image=img;
}
void DrawGraphics(Graphics g, Rectangle target)
{
// draw a red rectangle around the moon
g.DrawRectangle(Pens.Red, 130, 69, 8, 8);
}
}
So when you launch the application a red rectangle appears on the left only, because the button hasn't been pressed yet.
and when the button is pressed, the red rectangle appears on top of the image displayed in pictureBox2.
Nothing dramatic, but it does the job. So depending on the mode of operation you need (user graphics, or image annotations) use the example code to understand how to do it.

How to keep graphics when resizing C# form

I am working on a program where I need to draw rectangle graphics onto the form itself, when I click the form. I created code to do this (below), but when I resize the form the rectangles are deleted.
How do I retain the drawn rectangles when the form is resized?
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
Graphics g = this.CreateGraphics();
Pen Haitham = new Pen(Color.Silver, 2);
g.FillRectangle(Haitham.Brush, new Rectangle(e.X, e.Y, 50, 50));
}
You could do this instead:
private List<Point> _points = new List<Point>();
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
foreach(Point point in _points)
{
using (Pen Haitham = new Pen(Color.Silver, 2))
{
e.Graphics.FillRectangle(Haitham.Brush, new Rectangle(point.X, point.Y, 50, 50));
}
}
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
_points.Add(new Point(e.X, e.Y));
Invalidate(); // could be optimized to invalidate only the future rectangle draw
}
In Windows with Winforms (or native Windows), you supposed to override OnPaint and do almost all your paint logic there.
Note with WPF, it would be different, you would compose a scene adding elements to it (here you would add a Rectangle shape to a Canvas for example).
You must do the "Graphics" things in the "Paint" event. Then you can see your rectangle always, because the event fires whenever windows needed to invalidate the paintings.
Cheers
I'm not too terribly familiar with graphics, but I am assuming that you would need to put all of your drawing objects into a container and have them redrawn when the form is sized. You might need to recall all of your paining objects in the sizeChanged event.

Custom control onPaint event not working

Hey people I have a problem I am writing a custom control. My control inherits from Windows.Forms.Control and I am trying to override the OnPaint method. The problem is kind of weird because it works only if I include one control in my form if I add another control then the second one does not get draw, however the OnPaint method gets called for all the controls. So what I want is that all my custom controls get draw not only one here is my code:
If you run the code you will see that only one red rectangle appears in the screen.
public partial class Form1 : Form
{
myControl one = new myControl(0, 0);
myControl two = new myControl(100, 0);
public Form1()
{
InitializeComponent();
Controls.Add(one);
Controls.Add(two);
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
public class myControl:Control
{
public myControl(int x, int y)
{
Location = new Point(x, y);
Size = new Size(100, 20);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Pen myPen = new Pen(Color.Red);
e.Graphics.DrawRectangle(myPen, new Rectangle(Location, new Size(Size.Width - 1, Size.Height - 1)));
}
}
I'm guessing you are looking for something like this:
e.Graphics.DrawRectangle(Pens.Red, new Rectangle(0, 0,
this.ClientSize.Width - 1,
this.ClientSize.Height - 1));
Your Graphic object is for the interior of your control, so using Location isn't really effective here. The coordinate system starts at 0,0 from the upper-left corner of the client area of the control.
Also, you can just use the built-in Pens for colors, otherwise, if you are creating your own "new" pen, be sure to dispose of them.
LarsTech beat me to it, but you should understand why:
All drawing inside of a control is made to a "canvas" (properly called a Device Context in Windows) who coordinates are self-relative. The upper-left corner is always 0, 0.
The Width and Height are found in ClientSize or ClientRectangle. This is because a window (a control is a window in Windows), has two areas: Client area and non-client area. For your borderless/titlebar-less control those areas are one and the same, but for future-proofing you always want to paint in the client area (unless the rare occasion occurs where you want to paint non-client bits that the OS normally paints for you).

Categories