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);
}
}
}
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 created two Rectangles. I want to add events on them. For example when mouse hover on one, the hovered one will change color, can do resize or drag them(rectangles) to other place...
I was just wondering if I could control the drawn graphic, or it will like Microsoft Paint that after you painted, the object can not be operate unless you clear canvas and do redraw.
Is it possible to control a drawn graphic, thanks for any suggestion.
Simple code of my drawn graphics
private void Form1_Paint(object sender, PaintEventArgs e)
{
// Create pen.
Pen blackPen = new Pen(Color.Black, 3);
// Create rectangle.
Rectangle rect1 = new Rectangle(20, 20, 250, 250);
Rectangle rect2 = new Rectangle(70, 70, 150, 150);
// Draw rectangle to screen.
e.Graphics.FillRectangle(Brushes.DeepSkyBlue, rect1);
e.Graphics.FillRectangle(Brushes.LightBlue, rect2);
}
Also, you can create your own control like:
class RectangleControl : Control
{
public void FillRectangle(Color color)
{
this.BackColor = color;
}
}
Then :
private void Form1_Paint(object sender, PaintEventArgs e)
{
RectangleControl rect1 = new RectangleControl() { Parent = this, Left = 20, Top = 20, Width = 250, Height = 250 };
rect1.FillRectangle(Color.DeepSkyBlue);
RectangleControl rect2 = new RectangleControl() { Parent = rect1, Left = 50, Top = 50, Width = 150, Height = 150 };
rect2.FillRectangle(Color.LightBlue);
rect1.MouseHover += Rect1_MouseHover;
rect2.MouseLeave += Rect2_MouseLeave;
}
private void Rect2_MouseLeave(object sender, EventArgs e)
{
(sender as RectangleControl).BackColor = Color.Yellow;
}
private void Rect1_MouseHover(object sender, EventArgs e)
{
(sender as RectangleControl).BackColor = Color.LightBlue;
}
You can use Panel control instead.
Just add 2 panel controls as you would like them to be arranged and add 2 event handlers:
private void panel1_MouseHover(object sender, EventArgs e)
{
panel1.BackColor = Color.Yellow;
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
panel1.BackColor = Color.LightBlue;
}
You can monitor the MouseMove.
Here's code using MouseMove and some brush values used by the rectangles.
using System.Drawing;
using System.Windows.Forms;
namespace Question_Answer_WinForms_App
{
public partial class Form1 : Form
{
public Brush outerRectangleBrush = Brushes.DeepSkyBlue;
public Brush innerRectangleBrush = Brushes.LightBlue;
public Form1()
{
InitializeComponent();
Paint += Form1_Paint;
MouseMove += Form1_MouseMove;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
var isWithinOuterRectangle = e.Location.X >= 20
&& e.Location.X <= 250 + 20
&& e.Location.Y >= 20
&& e.Location.Y <= 250 + 20;
var isWithinInnerRectangle = e.Location.X >= 70
&& e.Location.X <= 150 + 70
&& e.Location.Y >= 70
&& e.Location.Y <= 150 + 70;
if (isWithinOuterRectangle)
{
if (isWithinInnerRectangle)
{
outerRectangleBrush = Brushes.DeepSkyBlue;
innerRectangleBrush = Brushes.Red;
Refresh();
}
else
{
outerRectangleBrush = Brushes.Red;
innerRectangleBrush = Brushes.LightBlue;
Refresh();
}
}
else
{
outerRectangleBrush = Brushes.DeepSkyBlue;
innerRectangleBrush = Brushes.LightBlue;
Refresh();
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// Create pen.
Pen blackPen = new Pen(Color.Black, 3);
// Create rectangle.
Rectangle rect1 = new Rectangle(20, 20, 250, 250);
Rectangle rect2 = new Rectangle(70, 70, 150, 150);
// Draw rectangle to screen.
e.Graphics.FillRectangle(outerRectangleBrush, rect1);
e.Graphics.FillRectangle(innerRectangleBrush, rect2);
}
}
}
#FJF, writing a ms-paint like application is not a complicated task. Windows Forms applications are using GDI+ to render graphics. so you can write a simple paint application using WindowsForms.
#nexolini uses a panel to do some draws. actually Ms-Paint does the same somehow. Ms-Paint is a Single-Layer Editor. So you can't resize all objects anytime you wanted (but as I said before you can assume that you have a panel for each newly drawn Shapes; Something like what Ms-Paint does).
So what is the problem?
Ms-Paint doesn't tracks your mouse movements and it doesn't needed (as it's a single layer). You can do all of it's tasks using these Answers.
e.g: for adding a Fill Color tool you can use a getpixel and putpixel to do a recursive algorithm on you image. and you are not needed to know which shape you are working on.
all the other tasks could be implemented easy.
for multi-layer Editors I will prefer to use a more powerful framework (but it's also could be implemented in Windows forms in a bad way), Like WPF. WPF uses DirectX to render your graphics and you are able to write an smooth Editor. WPF is designed to handle your graphical request so it does better on graphics.
#Reza_Aghaei 's comments are useful for Windows Forms.
New to stack overflow so hoping someone knows what's going wrong here.
For some reason whenever I hover over a button in my c# forms application, it causes a reset in my code. This seems weird to me considering I don't actually have any hover events on the button. I even tried adding another button and even when I do nothing to it, hovering still causes the reset.
The program is supposed to fill up the tank with colors and the color can be changed while it is filling up. Once it is filled up it resets to empty. It should start filling the next time the slider governing fill speed is interacted with.
It does this correctly but it also starts filling again when I hover over the button.
namespace Assignment5A
{
public partial class MainForm : Form
{
public float streamHeight = 370;
public float lastWaterHeight = 0;
public float waterHeight = 0;
public float waterBottom = 500;
public float fillSpeed = 300;
public Color brushColor = Color.LightBlue;
public Graphics g;
public Pen pen;
public Brush brush = new SolidBrush(Color.LightBlue);
public MainForm()
{
InitializeComponent();
this.Width = 500;
this.Height =600;
this.BackColor = Color.Black;
SpeedTimer.Interval = (int)fillSpeed;
}
private void MainForm_Paint(object sender, PaintEventArgs e)
{
g = e.Graphics;
pen = new Pen(Color.White);
g.DrawLine(pen, 50, 200, 50, 500);
g.DrawLine(pen, 350, 200, 350, 500);
g.DrawLine(pen, 50, 500, 350, 500);
SpeedTimer.Start();
}
private void SpeedTimer_Tick(object sender, EventArgs e)
{
if (waterHeight < 270)
{
brush = new SolidBrush(brushColor);
g = this.CreateGraphics();
g.FillRectangle(brush, 108, 136, 20, waterBottom - 136 - waterHeight);
waterHeight += 1f;
g.FillRectangle(brush, 51, waterBottom - waterHeight, 299, waterHeight - lastWaterHeight);
lastWaterHeight = waterHeight;
}
else
{
SpeedTimer.Stop();
waterHeight = 0;
lastWaterHeight = 0;
brush = new SolidBrush(Color.Black);
g.FillRectangle(brush, 51, 136, 299, 364);
}
}
private void Speed_Scroll(object sender, EventArgs e)
{
if (waterHeight < 270)
{
float scrollValue = Speed.Value;
fillSpeed = 300 / scrollValue;
SpeedTimer.Interval = (int)fillSpeed;
}
else
{
brush = new SolidBrush(Color.Black);
g.FillRectangle(brush, 51, 230, 299, 270);
SpeedTimer.Start();
}
}
private void ColorButton_Click(object sender, EventArgs e)
{
SetColor.ShowDialog();
brushColor = SetColor.Color;
}
}
}
OK, my first answer was completely wrong, must have read the question wrong, sorry.
The answer still comes down to your MainForm_Paint event, though. This event is fired whenever the form is drawn or redrawn, and includes anything on it (like your buttons). When your mouse hovers over an element it needs to be redrawn, as well as any of its parents, right back to the form level. It also occurs if the form is resized, comes back into view after being hidden (either partially or completely), goes off screen and comes back on, etc, etc. Lots of different things will trigger a form's Paint event to fire. And in your MainForm_Paint event, you have this line:
SpeedTimer.Start();
...which causes the timer to start and everything starts all over again.
Instead of using MainForm_Paint, I'd probably suggest you use the form's Load event to set up all these initial conditions. That will only fire once after the form has initialised and is shown on screen.
I am trying to draw circles using Bitmap.
Each time I click the mouse, the circle I previously drew is moved to the new position.
What I want to happen is: Each time I click the mouse, a new circle is created/drawn at the position I clicked and all previously drawn circles remain without moving.
I am working with the following code:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace multirectangle
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
Bitmap background;
Graphics scG;
Rectangle rectangleObj;
private Point clickCurrent = Point.Empty;
private Point clickPrev = Point.Empty;
private void Form1_Load(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
rectangleObj = new Rectangle(10, 10, 30, 30);
scG = Graphics.FromImage(background);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
if (clickPrev == Point.Empty) return;
rectangleObj.X = clickPrev.X - rectangleObj.Height / 2;// +radius;
rectangleObj.Y = clickPrev.Y - rectangleObj.Width / 2;
Refresh();
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.DrawImage(Draw(), 0, 0);
}
public Bitmap Draw()
{
Graphics scG = Graphics.FromImage(background);
Pen myPen = new Pen(System.Drawing.Color.Red, 3);
scG.Clear(SystemColors.Control);
scG.DrawEllipse(myPen, rectangleObj);
return background;
}
}
}
Your English was a little confusing. If I'm understanding your problem correctly, right now the only thing that's being drawn is the new circle where the click was, and you want all the old ones to persist as well? In which case, there are two options:
Don't clear the bitmap before you draw. scG.Clear(SystemColors.Control); will clear the bitmap you just drew. If you remove that line and don't clear the bitmap, then the next time you click, it will then draw the new ellipse right on top of the last bitmap.
If you want a fresh drawing/bitmap everytime, you would need a list of your rectangleObj . Each time you click, you add that point to your rectangleObj collection. Then in your draw method, you would iterate through the collection and draw all of them.
I notice a few things. First, in Form1_MouseDown(), you have this:
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
You are overwriting the old position (clickPrev) before you even save it. If you want to keep both positions, you should put them in a simple structure, like a List. When you get a new point, just Add() it to the list. Then, in your Draw() routine, loop over all the elements in the list and draw them all.
If you just want two positions--and only two--just swap your statements like this:
clickPrev = clickCurrent;
clickCurrent = PointToClient(Cursor.Position);
And you'll have to allocate another rectangle object for the drawing, although it would make more sense to take care of this in the Draw() routine.
Swap the position of the following statements
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
I think you are assigning the clickCurrent to clickPrevious after you initialize clickCurrent. It needs to be the other way.
Please try this
Rectangle rectangleObj;
Bitmap background;
Graphics scG;
Pen myPen;
private void Form1_Load(object sender, EventArgs e)
{
rectangleObj = new Rectangle(10, 10, 30, 30);
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
myPen = new Pen(Color.Red, 3);
BackgroundImage = background;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
var point = PointToClient(Cursor.Position);
rectangleObj.X = point.X - rectangleObj.Height / 2;
rectangleObj.Y = point.Y - rectangleObj.Width / 2;
scG.DrawEllipse(myPen, rectangleObj);
Refresh();
}
OnPaint and Draw methods removed. As well as clickCurrent and clickPrev fields.
When you change the form size (for example, maximize it), Bitmap and Graphics remain the same, so you get this effect. To avoid this, you need to add the event handler
private void Form1_SizeChanged(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
BackgroundImage = background;
}
Note that each time you resize the form, all previously drawn is erased. If this is undesirable, a different approach is needed for drawing. Let me know, I will give another example.
The problem isn't that I don't know how to make a border-less form re-sizable, or to how to draw a border. The problem is what happens when you re-size the form with that custom border.
Here is a screenshot, because I don't know how to explain it:
Here is how I created the border (currently):
private void Form1_Paint(object sender, PaintEventArgs e)
{
int width = 1;
Rectangle rec = this.ClientRectangle;
ButtonBorderStyle bbs = ButtonBorderStyle.Solid;
Color clr = Color.Gray;
ControlPaint.DrawBorder(e.Graphics, rec, clr, width, bbs, clr, width, bbs, clr, width, bbs, clr, width, bbs);
}
As for re-sizing a border-less form; I created a repository for the project.
Resize Custom Border - Bitbucket
I don't have any idea as to why this happens, so I wouldn't know where to begin. I just need to draw a border without it doing this. I have tried other ways of drawing one, but the results were the same.
Hopefully this and the repository becomes useful for anyone trying to do the same.
Thank you for taking your time to read if you did.
Try to use Graphics.DrawRectangle instead of DrawBorder
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Single fWidth = 5.0f;
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, fWidth), r);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Invalidate();
}
Use Graphic library :
Step 1: Override the OnPaint handler for your main form
Step 2: Define a rectangle that covers your current form
Step 3: Draw the defined rectangle
protected override void OnPaint(PaintEventArgs e)
{
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, 1.0f), r);
}
You may also implement this using a condition statement like:
this.form.Resize += // some handler1
//in hadler1
{
this.form.Paint += // Your new paint handler2
}
//in handler2
{
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, 1.0f), r);
}