Clicking a button from windows form it doesn't work.
Because, I have to use the parameters "Graphics g..." for the methods. I can't change the methods so, I have to change something when the user clicks the button.
Below is the code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//protected override void OnPaint(PaintEventArgs e)
//{
// Graphics g = e.Graphics;
// TekenCirkel(g, 50, 50, 100, 100);
// TekenRechthoek(g, 0, 0, 100, 100);
//}
public void TekenCirkel(Graphics g, int x, int y, int w, int h) {
Pen cirkel = new Pen(Color.Blue, 2);
g.DrawEllipse(cirkel, x,y, w, h);
}
public void TekenRechthoek(Graphics g, int x, int y, int w, int h){
Pen rechthoek = new Pen(Color.Black, 2);
g.DrawRectangle(rechthoek, x, y, w, h);
}
private void Cirkel_Click(object sender, EventArgs e)
{
TekenCirkel(g, 50, 50, 100, 100);
}
}
As you can see I tested it with the onPaint method and defining what g does. When using this method the code works. But that isn't possible when trying to click the button. Because EventArgs and PaintEventArgs are different things.
E: You click a button and a cirkel/square gets painted on the form. I want to know how to call a method I created to draw the cirkel/ square.
That's not how painting works.
You must paint everything from OnPaint() only, using data structures in your class to track what you want to paint.
You then call Invalidate() to make it repaint if you want to change it.
Related
I have a function which is drawing an ellipse. I want to make the previously drawn ellipse invisible by changing its color as the same color of its background when a new ellipse is drawn by changing the form size.
This is my function in my class:
class ClassClock
{
public static void drawClock(Point m, int s, Form frm, Color myColor)
{
Graphics paper = frm.CreateGraphics();
Pen myPen = new Pen(myColor);
int w = frm.ClientSize.Width;
int h = frm.ClientSize.Height;
m = new Point(w / 2, h / 2);
s = Math.Min(w, h) / 2;
paper.DrawEllipse(myPen, m.X - s, m.Y - s, s * 2, s * 2);
}
}
and this is my timer:
private void timer1_Tick(object sender, EventArgs e)
{
ClassClock.drawClock(m, s, this, this.BackColor);
ClassClock.drawClock(m, s, this, Color.Black);
}
Can someone help me find a solution to this?
You should not use CreateGraphics like this. Instead, override the OnPaint method of your form and do all your painting in that method.
Windows uses an immediate mode graphics system. That means that once your ellipse is drawn, it's gone, save for the pixels currently on the screen. If the window is minimized or another window is dragged across it, the ellipse will be gone and will have to be repainted. That is what the OnPaint method is for.
Here's a simple form that changes the color of your circle when a button is clicked. To run this code, you will need to add a button to the form and name it btnChangeColor.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//Property to hold the current color. This could be a private field also.
public Color CurrentColor { get; set; } = Color.Red;
//Used to generate a random number when the button is clicked.
private Random rnd = new Random();
//All painting of the form should be in this method.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
//Use the graphics event provided to you in PaintEventArgs
Graphics paper = e.Graphics;
int w = this.ClientSize.Width;
int h = this.ClientSize.Height;
Point m = new Point(w / 2, h / 2);
int s = Math.Min(w, h) / 2;
//It is important to dispose of any pens you create
using (Pen myPen = new Pen(CurrentColor))
{
paper.DrawEllipse(myPen, m.X - s, m.Y - s, s * 2, s * 2);
}
}
//When the button is clicked, the `CurrentColor` property is set to a random
//color and the form is refreshed to get it to repaint itself.
private void btnChangeColor_Click(object sender, EventArgs e)
{
//Change the current color
CurrentColor = Color.FromArgb(255, rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256));
//Refresh the form so it repaints itself
this.Refresh();
}
}
I'm using an eye tracker to display eye movements on a form. The movements have been flickering a lot so I found out I can use BufferedGraphics which all works fine except when the eye movement starts it turns the form from the original colour to black. This is the code. Hopefully someone can help!
private void button2_Click(object sender, EventArgs e)
{
var host = new Host();
var gazeStream = host.Streams.CreateGazePointDataStream();
gazeStream.GazePoint((x, y, ts)
=> drawCircle(new PointF((float)x, (float)y)));
}
delegate void SetCallback(PointF point);
private void drawCircle(PointF point)
{
float x = point.X;
float y = point.Y;
if (this.InvokeRequired)
{
SetCallback d = new SetCallback(drawCircle);
this.Invoke(d, new object[] { point });
}
else
{
SolidBrush semiTransBrush = new SolidBrush(Color.Coral);
Pen pen = new Pen(Color.Aquamarine, 2);
BufferedGraphicsContext currentContext;
BufferedGraphics myBuffer;
// Gets a reference to the current BufferedGraphicsContext
currentContext = BufferedGraphicsManager.Current;
// Creates a BufferedGraphics instance associated with Form1, and with
// dimensions the same size as the drawing surface of Form1.
myBuffer = currentContext.Allocate(this.CreateGraphics(),this.DisplayRectangle);
myBuffer.Graphics.DrawEllipse(pen, x, y, 100, 100);
myBuffer.Graphics.FillEllipse(semiTransBrush, x, y, 100, 100);
// Renders the contents of the buffer to the specified drawing surface.
myBuffer.Render(this.CreateGraphics());
myBuffer.Dispose();
}
You can see in the image that the circle appears behind the controls which seems like the form is gone?
When you allocate the buffer, it creates a compatible rendering surface with the graphics you provided. But it will not copy it or anything so if you just paint a single circle, the remaining parts remain black.
BufferedGraphics really can help you to avoid flickering in special cases (eg. when system double buffering must be disabled for some reason), but here this is an overkill.
So the key is just enabling double buffering and do every paint in the Paint event (or OnPaint method). In your code you do immediate paint, which always flickers. Instead, you should just invalidate the form and let the system do a regular repaint session, which can use double buffering if you wish.
Into the constructor:
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
Then modify the click event:
private PointF lastGazePoint;
private void button2_Click(object sender, EventArgs e)
{
var host = new Host();
var gazeStream = host.Streams.CreateGazePointDataStream();
gazeStream.GazePoint((x, y, ts) =>
{
lastGazePoint = new PointF((float)x, (float)y);
Invalidate();
});
}
The painting itself:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawCircle(e.Graphics, lastGazePoint.X, lastGazePoint.Y);
}
And finally, modify DrawCircle to use the Graphics from PaintEventArgs:
private void DrawCircle(Graphics g, float x, float y)
{
using (Brush semiTransBrush = new SolidBrush(Color.Coral))
{
using (Pen pen = new Pen(Color.Aquamarine, 2))
{
g.DrawEllipse(pen, x, y, 100, 100);
g.FillEllipse(semiTransBrush, x, y, 100, 100);
}
}
}
I use the following code to fill up a panel.But whenever i minimize the form, the filled up portion of the rectangle gets lost.
Any ideas what I'm doing wrong? Thanks
public static void populateTable(this Panel p, int x, int y)
{
Graphics g = p.CreateGraphics();
Brush b = new SolidBrush(Color.DarkCyan);
g.FillRectangle(b, x, y,100,40);
g.Dispose();
}
You need to repaint every time the panel repaints itself (For example when you restore the window from being minimized). The correct way to do this is use the Paint event and store the objects you need to draw in some form of collection and re-draw them every call.
public Form1()
{
InitializeComponent();
rectangles = new List<Rectangle>();
panel1.Paint += panel1_Paint;
}
public void PopuplateTable(int x, int y)
{
rectangles.Add(new Rectangle(x,y, 100, 40));
//Forces a redraw to happen.
panel1.Invalidate();
}
private List<Rectangle> rectangles;
void panel1_Paint(object sender, PaintEventArgs e)
{
foreach (var rectangle in rectangles)
{
using (var b = new SolidBrush(Color.DarkCyan))
{
e.Graphics.FillRectangle(b, rectangle);
}
}
}
Now this is not exactly the same as your current code, but it points you in the right direction so you can adapt your code to do the same. You may need to create a new class based on Panel to hold your PopulateTable call instead of using a extension method, if you do end up doing that you should override OnPaint instead of using the paint event.
class MyPanel : Panel
{
private Rectangle? paintedRectangle = null;
public void PopuplateTable(int x, int y)
{
paintedRectangle = new Rectangle(x, y, 100, 40);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (paintedRectangle.HasValue)
{
using (var b = new SolidBrush(Color.DarkCyan))
{
e.Graphics.FillRectangle(b, paintedRectangle.Value);
}
}
}
}
I'm making a simple game in winform (tic-tac-toe), and I'm having some problem to paint block control.
Here is the class I made, that represent a block in the game (without game logic, is only UI).
public class UI_Block : Control
{
private Rectangle block;
private SIGNS sign;
public SIGNS Sign
{
get {return sign;}
set
{
if (sign == SIGNS.EMPTY)
sign = value;
}
}
public UI_Block( ) {
sign = SIGNS.EMPTY;
}
public void SetBlockOnBoard(int x, int y)
{
this.Location = new Point( x , y );
this.Size = new Size(Parent.Width /3, Parent.Height / 3);
block = new Rectangle(this.Location, this.Size);
}
public void DrawSign(Graphics g)
{
Pen myPen = new Pen(Color.Red);
if (sign == SIGNS.O)
{
drawO(g,new Pen(Brushes.Black));
}
if (sign == SIGNS.X)
{
drawX(g, new Pen(Brushes.Red));
}
}
protected override void OnPaint(PaintEventArgs e)
{
DrawSign(e.Graphics);
base.OnPaint(e);
}
//Draw X
private void drawX(Graphics g, Pen myPen)
{
//draw first daignol
Point daignolStart = new Point { X = this.Location.X , Y = this.Location.Y };
Point daignolEnd = new Point { X = this.Size.Width , Y = this.Size.Height };
g.DrawLine(myPen, daignolStart, daignolEnd);
//draw second daignol
daignolStart = new Point { X = Size.Width , Y = this.Location.Y };
daignolEnd = new Point { X = Location.X, Y = Size.Height };
g.DrawLine(myPen, daignolEnd, daignolStart);
}
//Draw O
private void drawO(Graphics g, Pen myPen)
{
g.DrawEllipse(myPen, block);
}
}
I added them both to the winForm class and to see how it looks like when I paint them:
public partial class Form1 : Form
{
UI.UI_Block block;
UI.UI_Block blockX;
public Form1()
{
InitializeComponent();
block = new UI.UI_Block();
blockX = new UI.UI_Block();
Controls.Add(block);
Controls.Add(blockX);
}
protected override void OnLoad(EventArgs e)
{
block. SetBlockOnBoard(0, 0);
blockX.SetBlockOnBoard(0, block.Height);
block.Sign = SIGNS.X;
blockX.Sign = SIGNS.O;
base.OnLoad(e);
}
protected override void OnPaint(PaintEventArgs e)
{
//block.DrawSign(e.Graphics);
//block.DrawSign(e.Graphics);
base.OnPaint(e);
}
}
I tried few things, like not using the onPaint event and I still get the same result.
Here what I see when I run it:
Any idea why I can't paint both of them?
You are not drawing the contents of your control in it's visible area, so it is drawing fine but you can't see it.
Every control has it's own coordinate space (client coords), which starts at 0,0 regardless of where it is positioned within the parent control. You are placing the control in it's parent correctly by setting its Location, but then you are also using the Location to offset the graphics, so they are essentially offset twice.
(If you make your control bigger you'll be able to see the X being drawn further down the screen)
To fix this, do all your drawing in the client coordinate space of your control, i.e. draw in the area (0, 0, width, height)
(P.S. You could just draw all 9 tiles in the parent control, which is a more efficient approach than creating 9 child controls. But what you are doing will work fine)
I'm trying to make a basic soccer game in C#, and ive almost completed the field except for the various arcs and circles that are fairly important in the game, especially to set the bounds the computer's players cannot pass while their teammate/opponent is lining up for the kick.
So, all the methods I've tried havent worked because apparently I'm using fields like types, but I'm copying the code exactly. But I don't think its very important to show the buggy code, for a start I deleted it and I'd like the circles to be there permanently, not from when debugging starts.
So this is what I need: panels with round borders that stay round, and a way to put it in my code, which I'll post if necessary. Visual Studio C# Express 2010.
All help appreciated, thanks
One easy way to draw a circle on a panel is to inherit from Panel and override the OnPaint method. In this method you would call DrawEllipse on the Graphics object gotten from the event args. On point of interest is that the size is set to Width-1 and Height-1. This stops the right and bottom of the circle from dissapearing out of the Panel control.
One enhancement I have put in this code is to constrain the width & height in the OnResize method, this ensures your panel is always a circle, in oppose to an Ellipse (which can have different width and height). Simply drag this control onto a windows form and have a play in the designer.
public class CirclePanel : Panel
{
public CirclePanel()
{
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawEllipse(Pens.Black, 0,0,this.Width-1,this.Height-1);
}
protected override void OnResize(EventArgs e)
{
this.Width = this.Height;
base.OnResize(e);
}
}
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Pen mypen;
Color mycolor=Color.Red;
Graphics mygraph;
int xloc = 50, yloc = 50;
public Form1()
{
InitializeComponent();
mygraph = CreateGraphics();
}
private void Form1_Load(object sender, EventArgs e)
{
}
float x = 270, y = 0.5f;
int xmover = 100, ymover = 48;
private void timer1_Tick(object sender, EventArgs e)
{
mygraph.DrawEllipse(new Pen(Color.Red), xloc, yloc, 102, 102);
mygraph.FillEllipse(new SolidBrush(Color.Red), xmover++, ymover++, 4, 4);
mycolor = this.BackColor;
mygraph.DrawPie(new Pen(mycolor), xloc+1, yloc+1, 100, 100, x-1, y);
mygraph.DrawPie(new Pen (Color.Red), xloc + 1, yloc + 1, 100, 100, x++, y);
mygraph.FillEllipse(new SolidBrush(mycolor), xmover-1, ymover - 1, 4, 4);
mygraph.DrawEllipse(new Pen(Color.Red), 100, 50, 5, 5);
}
}
}