I have an abstract class "Element" which its instances have to use the function DrawRectangle() from System.Drawing.
The problem is that because the class is abstract it can't inherit from FORM so the program doesn't recognize the classes..
How can I solve this?
I've tried puting a variable of type Form inside Element and didn't work.
I also tried the opposite, puting an Element variable inside Form.
It is my first time with Winform and C#...
It is supposed to be a mouse race game, the objects drawn in page are supposed to chase the mouse or go in other directions but I need it to be object oriented.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace MyMouseGame
{
public abstract class Element
{
//Form1 myForm = new Form1();// not sure I need it
public enum EnumShape { Square, Circle, Triangle }
public enum EnumType { Chase, Escape, Random }
//variables
private double X { get; set; }
private double Y { get; set; }
private int Size { get; }
private int Speed { get; }
private EnumShape Shape { get; set; }
protected EnumType Type { get; set; }
private static Random R = new Random();
//methods
public int generateRandomNum()
{
int randomNum = R.Next(20, 100);
return randomNum;
}
public Element()
{
X= 0;
Y = 0;
Shape = 0;
Size = generateRandomNum();
Speed = generateRandomNum();
Type = 0;
}
public void ElementsFactory()
{
}
public void hitTarget(e) { }
public abstract void Draw(Form1 g); //here I tried to pass a form inside
public abstract void Move();
}
}
//example of one of the instances classes of element
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace MyMouseGame
{
class E_Escape : Element
{
int move_circleX = 0;
int move_circleY = 0;
bool flag = false;
Rectangle circle = new Rectangle(10, 70, 35, 35);
Pen p = new Pen(Color.Black);
//circle
public E_Escape()
{
}
public override void Draw(Form1 g) {
g.DrawEllipse(p, circle);
g.FillEllipse(new SolidBrush(Color.Red), circle);
}
public override void Move() {
if (!flag)
{
if (circle.X >= panel1.Width - 2)
flag = true;
circle.X += 10;
}
else
{
if (circle.X <= 30)
flag = false;
circle.X -= 10;
}
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace MyMouseGame
{
public partial class Form1 : Form
{
Graphics g;
Element [] elements= new Element[3];
public Form1()
{
this.DoubleBuffered = true;
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
g = panel1.CreateGraphics();
//timer set
t.Interval = 100;
t.Tick += new EventHandler(timer1_Tick);
t.Start();
}
private void recThreadTracker()
{
//move rectangle
while (true)//here I tried to call element finction draw
{
elements[0].Draw(g);
elements[1].Draw(g);
elements[2].Draw(g);
}
}
public void threadCounter()
{
long counter = 0;
while (true)
{
counter++;
this.Invoke(new Action(() =>
{
lcountRes.Text = counter.ToString();
}));
Thread.Sleep(1000);
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
Thread counterThread = new Thread(threadCounter);
counterThread.Start();
Thread recTrackThread = new Thread(recThreadTracker);
recTrackThread.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
foreach (Element elements in elements)
{
elements.Move();
}
//Refresh();
Invalidate();
}
}
}
The drawing routine needs two things. A Graphics object, and the Size of the drawing area.
For example, to draw a circle on the center of the graphics area
public void Draw(Graphics g, Size target)
{
int diameter = 16;
g.DrawEllipse(Pens.Black, target.Width/2 - diameter/2, target.Height/2 - diameter/2, diameter, diameter);
}
And in your form's paint event you call all the draw functions
void panel1_Paint(object sender, PaintEventArgs e)
{
foreach(var item in Elements)
{
item.Draw(e.Graphics, pictureBox1.ClientSize);
}
}
Related
I'm working on a base for a small simple game-style program and I'm using Form1_Resize to control the scale and location of the premade controls i've placed. Currently they're just a picturebox and two blank buttons for testing however they scale and work perfect when the screen is being resized in this method.
I added a custom control to place images with transparency, and it functions fine as well however when I resize the screen the image from the custom control will disappear. I know it's to do with it being painted over and not being repainted when the screen is being resized. By telling it to refresh in the form1_Resize method it does reappear after resizing is done but still is gone while being resized. I've tried everything I could find really but this is still mostly new to me and i'm having no luck.
Here are my classes below. TestControl is basically a combination of the ScreenDrawing and DrawControl classes with a few tweaks as a test however it didn't seem to make any difference at all.
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace List_Game
{
public partial class Form1 : Form
{
int baseFormWidth = 1280;
int baseFormHeight = 720;
int baseStartButtonWidth = 250;
int baseStartButtonHeight = 100;
int baseSettingsButtonWidth = 250;
int baseSettingsButtonHeight = 60;
// When resized, difference of form size and window size
int windowWidthOffset;
int windowHeightOffset;
//Position of button box to center it when scaling screen
int startButtonXOffset;
int startButtonYOffset;
int settingsButtonXOffset;
int settingsButtonYOffset;
public Form1()
{
InitializeComponent();
windowWidthOffset = this.Width - GameWindow.Width;
windowHeightOffset = this.Height - GameWindow.Height;
}
float WidthScale()
{
return (float)this.Width / (float)baseFormWidth;
}
float HeightScale()
{
return (float)this.Height / (float)baseFormHeight;
}
private void Form1_Resize(object sender, EventArgs e)
{
GameWindow.Width = this.Width - windowWidthOffset;
GameWindow.Height = this.Height - windowHeightOffset;
float scaledStartButtonWidth = baseStartButtonWidth * WidthScale();
float scaledStartButtonHeight = baseStartButtonHeight * HeightScale();
StartButton.Width = (int)scaledStartButtonWidth;
StartButton.Height = (int)scaledStartButtonHeight;
float scaledSettingsButtonWidth = baseSettingsButtonWidth * WidthScale();
float scaledSettingsButtonHeight = baseSettingsButtonHeight * HeightScale();
SettingsButton.Width = (int)scaledSettingsButtonWidth;
SettingsButton.Height = (int)scaledSettingsButtonHeight;
startButtonXOffset = GameWindow.Width / 2 - StartButton.Width / 2;
startButtonYOffset = (int)(GameWindow.Height / 1.44) - StartButton.Height / 2;
settingsButtonXOffset = GameWindow.Width / 2 - (SettingsButton.Width / 2);
settingsButtonYOffset = GameWindow.Height - (int)(SettingsButton.Height * 1.25);
Point startButtonLocation = new Point(startButtonXOffset, startButtonYOffset);
Point settingsButtonLocation = new Point(settingsButtonXOffset, settingsButtonYOffset);
StartButton.Location = startButtonLocation;
SettingsButton.Location = settingsButtonLocation;
}
private void Settings_Click(object sender, EventArgs e)
{
}
private void Start_Click(object sender, EventArgs e)
{
}
}
}
ScreenDrawing.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace List_Game
{
abstract public class ScreenDrawing : Panel
{
protected Graphics graphics;
abstract protected void OnDraw();
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Update the private member so we can use it in the OnDraw method
this.graphics = e.Graphics;
// Set the best settings possible (quality-wise)
this.graphics.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.AntiAlias;
this.graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
this.graphics.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
this.graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.HighQuality;
// Calls the OnDraw subclass method
OnDraw();
}
}
}
DrawControl.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace List_Game
{
class DrawControl : ScreenDrawing
{
Image displayedImage = null;
int width;
int height;
Rectangle big;
public void SetImage()
{
if (this.BackgroundImage == null)
{
displayedImage = global::List_Game.Properties.Resources.StartButton2;
}
else displayedImage = this.BackgroundImage;
}
protected override void OnDraw()
{
SetImage();
// Sets the images' sizes and positions
width = displayedImage.Size.Width;
height = displayedImage.Size.Height;
this.Width = width;
this.Height = height;
big = new Rectangle(0, 0, width, height);
// Draws the two images
this.graphics.DrawImage(displayedImage, big);
}
}
}
TestControl.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace List_Game
{
public class TransparentControl : Control
{
private readonly Timer refresher;
private Image _image = global::List_Game.Properties.Resources.StartButton2;
public TransparentControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
refresher = new Timer();
refresher.Tick += TimerOnTick;
refresher.Interval = 50;
refresher.Enabled = true;
refresher.Start();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
protected override void OnPaint(PaintEventArgs e)
{
if (_image != null)
{
e.Graphics.DrawImage(_image, (Width / 2) - (_image.Width / 2), (Height / 2) - (_image.Height / 2));
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//Do not paint background
if (_image != null)
{
e.Graphics.DrawImage(_image, (Width / 2) - (_image.Width / 2), (Height / 2) - (_image.Height / 2));
}
}
//Hack
public void Redraw()
{
RecreateHandle();
}
private void TimerOnTick(object source, EventArgs e)
{
RecreateHandle();
refresher.Stop();
}
public Image Image
{
get
{
return _image;
}
set
{
_image = global::List_Game.Properties.Resources.StartButton2;
RecreateHandle();
}
}
}
}
first of all i want you to know that i know that there a lot of results for this question, but i have searched far and wide still haven't come up with a solution for my problem.
i have tried to do the following:
1.constructor
2.objects
3.properties
4.delegates
but none of my implementation of them really did worked as wanted (in this "solution" i have used properties
when i press "back" on the "pop up" screen i dont in the main screen the value i choose from in the "pop up" screen
basically, it's something like, i have main screen and a "pop up"
the main screen
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BakaritCV
{
public partial class FrmProdChoose : MetroFramework.Forms.MetroForm
{
CVFeedUtilities utilities = new CVFeedUtilities();
Mixtures mixture;
public string selectedDefault = " 102";
string t;
public FrmProdChoose(string t)
{
InitializeComponent();
this.t = t;
}
public FrmProdChoose()
{
InitializeComponent();
}
private void btnHome_Click(object sender, EventArgs e)
{
FrmMain frmload = new FrmMain();
utilities.moveBetweenScreens(this, frmload);
}
private void mixtureBtn_Click(object sender, EventArgs e)
{
utilities.loadPopUp(this, mixture);
}
private void FrmProdChoose_Load(object sender, EventArgs e)
{
mixture = new Mixtures(this);
mixtureBtn.Text = selectedDefault;
}
public string Selected
{
get { return selectedDefault; }
set { selectedDefault = value; }
}
}
}
the "pop up"
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BakaritCV
{
public partial class Mixtures : MetroFramework.Forms.MetroForm
{
string[] mixture = new string[] { "102", "103", "104", "105" };
MetroFramework.Controls.MetroTile[] tiles;
FrmProdChoose form;
string selectedDefault;
CVFeedUtilities utilities = new CVFeedUtilities();
public Mixtures(FrmProdChoose form)
{
InitializeComponent();
this.form = form;
}
private void btnHome_Click(object sender, EventArgs e)
{
form.Selected = selectedDefault;
utilities.closePopUp(this, form);
}
private void Mixtures_Load(object sender, EventArgs e)
{
tiles = new MetroFramework.Controls.MetroTile[] { tileOne, tileTwo, tileThree, tileFour};
for (int i = 0; i < mixture.Length; i++)
tiles[i].Text = mixture[i];
}
private void tileOne_Click(object sender, EventArgs e)
{
tileOne.BackColor = Color.ForestGreen;
removeBackColor(1);
}
private void tileTwo_Click(object sender, EventArgs e)
{
tileTwo.BackColor = Color.ForestGreen;
removeBackColor(2);
}
private void tileThree_Click(object sender, EventArgs e)
{
tileThree.BackColor = Color.ForestGreen;
removeBackColor(3);
}
private void tileFour_Click(object sender, EventArgs e)
{
tileFour.BackColor = Color.ForestGreen;
removeBackColor(4);
}
private void tileFive_Click(object sender, EventArgs e)
{
tileFive.BackColor = Color.ForestGreen;
removeBackColor(5);
}
public void removeBackColor(int index)
{
for (int i = 0; i < tiles.Length; i++)
{
if (i == index - 1)
{
selectedDefault = tiles[i].Text;
continue;
}
else tiles[i].BackColor = Color.DeepSkyBlue;
}
}
}
}
and the functions loadPopUp and closePopUp
public void loadPopUp(Form from, Form to)
{
to.Tag = from;
to.Show(from);
}
public void closePopUp(Form from, Form to)
{
to.Tag = from;
if (!to.Visible)
to.Show(from);
from.Hide();
}
I'm trying to understand the lock mechanism.
if I have multiple events to lock on different value should I use an object lock for each?
More serious code added:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace ValueChangeOnEventForm
{
public partial class Form1 : Form
{
private Test_Onchange DataSource;
Thread Task1;
private bool Flag_Stop_Task1;
public Form1()
{
InitializeComponent();
graph1.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
graph1.ChartAreas[0].AxisX.IsLabelAutoFit = true;
graph1.ChartAreas[0].AxisX.ScaleView.Size = 100;
graph2.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
graph2.ChartAreas[0].AxisX.IsLabelAutoFit = true;
graph2.ChartAreas[0].AxisX.ScaleView.Size = 100;
DataSource = new Test_Onchange();
DataSource.ValueChanged += new EventHandler(EventValueChange);//Value input info
DataSource.SecondValueChange += new EventHandler(EventSecondValueChange);//second value
Task1 = new Thread(new ThreadStart(Task_1));//create the thread
Task1.Start();//start the thread
}
protected virtual void EventSecondValueChange(object sender, EventArgs e)
{
double valueMAX = 0, size = 0;
if (graph1.InvokeRequired)
{
graph1.Invoke(new MethodInvoker(delegate { graph1.Series["ValueOnGraph"].Points.AddY(DataSource.Value); }));
graph1.Invoke(new MethodInvoker(delegate { valueMAX = graph1.ChartAreas[0].AxisX.Maximum; }));
graph1.Invoke(new MethodInvoker(delegate { size = graph1.ChartAreas[0].AxisX.ScaleView.Size; }));
if (valueMAX - 10 > size)
{
graph1.Invoke(new MethodInvoker(delegate { graph1.ChartAreas[0].AxisX.ScaleView.Scroll(graph1.ChartAreas[0].AxisX.Maximum); }));
graph1.Invoke(new MethodInvoker(delegate { graph1.Series["ValueOnGraph"].Points.RemoveAt(0); }));
}
}
}
protected virtual void EventValueChange(object sender, EventArgs e)
{
double valueMAX=0,size=0;
if (graph2.InvokeRequired)
{
graph2.Invoke(new MethodInvoker(delegate { graph2.Series["ValueOnGraph2"].Points.AddY(DataSource.Secondvalue); }));
graph2.Invoke(new MethodInvoker(delegate { valueMAX = graph2.ChartAreas[0].AxisX.Maximum; }));
graph2.Invoke(new MethodInvoker(delegate { size = graph2.ChartAreas[0].AxisX.ScaleView.Size; }));
if (valueMAX - 10 > size)
{
graph2.Invoke(new MethodInvoker(delegate { graph2.ChartAreas[0].AxisX.ScaleView.Scroll(graph2.ChartAreas[0].AxisX.Maximum); }));
graph2.Invoke(new MethodInvoker(delegate { graph2.Series["ValueOnGraph2"].Points.RemoveAt(0); }));
}
}
}
private void Task_1()
{
while (!Flag_Stop_Task1)
{
Random RandVal = new Random();
Random RandVal2 = new Random();
int Value = RandVal.Next(0, 100);
int SecondValue = RandVal2.Next(50, 200);
DataSource.Value = Value;
DataSource.Secondvalue = SecondValue;
Thread.Sleep(100);
}
Flag_Stop_Task1 = false;
}
private void btn_StopTask_1_Click(object sender, EventArgs e)
{
Flag_Stop_Task1 = true;
}
}
}
And then
namespace ValueChangeOnEventForm
{
class Test_Onchange
{
private int value;
private int secondvalue;
protected object _lock = new object();
public event System.EventHandler ValueChanged;
public event System.EventHandler SecondValueChange;
protected virtual void OnValueChange()
{
lock (this._lock)
{
EventHandler eventvaluechange = ValueChanged;
if (eventvaluechange != null)
eventvaluechange(this, EventArgs.Empty);
}
}
protected virtual void OnSecondValueChange()
{
lock (this._lock)
{
EventHandler eventvaluechange = SecondValueChange;
if (eventvaluechange != null)
eventvaluechange(this, EventArgs.Empty);
}
}
public int Value
{
get { return this.value; }
set
{
if (value != this.value)
{//if value changed enter
this.value = value;
OnValueChange();
}
}
}
public int Secondvalue
{
get { return this.secondvalue; }
set
{
if (value != this.secondvalue)
{//if value changed enter
this.secondvalue = value;
OnSecondValueChange();
}
}
}
}
}
Do I need two lock (lock1 and lock2 object or only one for both value and secondvalue....?
Thanks a lot.
Update
Ok let's do it so.
I'm using beckhoff PLC which are real time Task PLC. and I'm reading two value on it when the value change. like this:
Form1 Class:
namespace RealTimeLock
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public partial class Form1 : Form
{
private PLC PLCData;
public Form1()
{
InitializeComponent();
}
public Form1(IBAAdsServer _adsServer)
: this()
{
PLCData = new PLC(_adsServer);
PLCData.ErrorBoolChanged += new EventHandler(EventErrorChanged);//error info
PLCData.ForceValChanged += new EventHandler(EventForceChanged);//Force input info
}
protected virtual void EventErrorChanged(object sender, EventArgs e)
{
//state of error PLC
lv_ErrorInfo.Text = "PLC Error num : " + PLCData.i_ErrorID.ToString();
}
protected virtual void EventForceChanged(object sender, EventArgs e)
{//modify graphical data PLC Force data
lv_ForceInfo.Text = PLCData.i_ForceVal.ToString();
c_graphForceIN.Series["ForceData"].Points.AddY(PLCData.i_ForceVal);
if (c_graphForceIN.ChartAreas[0].AxisX.Maximum - 10 > c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Size)
{
c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Scroll(c_graphForceIN.ChartAreas[0].AxisX.Maximum);
c_graphForceIN.Series["ForceData"].Points.RemoveAt(0);
}
}
}
}
Error ID and Force change showed in Form1 label lv_ErrorID and lv_Force and graphForceIN add point.
The events handler on the other side (PLC class) looks like this:
PLC Class:
namespace RealTimeLock
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public partial class Form1 : Form
{
private PLC PLCData;
public Form1()
{
InitializeComponent();
}
public Form1(IBAAdsServer _adsServer)
: this()
{
PLCData = new PLC(_adsServer);
PLCData.ErrorBoolChanged += new EventHandler(EventErrorChanged);//error info
PLCData.ForceValChanged += new EventHandler(EventForceChanged);//Force input info
}
protected virtual void EventErrorChanged(object sender, EventArgs e)
{
//state of error PLC
lv_ErrorInfo.Text = "PLC Error num : " + PLCData.i_ErrorID.ToString();
}
protected virtual void EventForceChanged(object sender, EventArgs e)
{//modify graphical data PLC Force data
lv_ForceInfo.Text = PLCData.i_ForceVal.ToString();
c_graphForceIN.Series["ForceData"].Points.AddY(PLCData.i_ForceVal);
if (c_graphForceIN.ChartAreas[0].AxisX.Maximum - 10 > c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Size)
{
c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Scroll(c_graphForceIN.ChartAreas[0].AxisX.Maximum);
c_graphForceIN.Series["ForceData"].Points.RemoveAt(0);
}
}
}
}
Does it seem to be correct coding for you guys? and while I have a real time task running there do I need to lock variables and if so, do I need two lock or only one??
Thanks for your remark on this!!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Engine : Form
{
int x, y;
int w, h;
Boolean running = false;
public Engine()
{
InitializeComponent();
}
private void Engine_Load(object sender, EventArgs e)
{
}
private void Engine_Paint(object sender, PaintEventArgs e)
{
}
private void XTB_TextChanged(object sender, EventArgs e)
{
if (XTB.Text.Equals("10"))
{
x = 10;
}
if (XTB.Text.Equals("20"))
{
x = 20;
}
}
private void YTB_TextChanged(object sender, EventArgs e)
{
if (YTB.Text.Equals("10"))
{
y = 10;
}
}
private void WTB_TextChanged(object sender, EventArgs e)
{
if (WTB.Text.Equals("8"))
{
w = 8;
}
if (WTB.Text.Equals("16"))
{
w = 16;
}
if (WTB.Text.Equals("32"))
{
w = 32;
}
}
private void HTB_TextChanged(object sender, EventArgs e)
{
if (HTB.Text.Equals("8"))
{
h = 8;
}
if (HTB.Text.Equals("16"))
{
h = 16;
}
if (HTB.Text.Equals("32"))
{
h = 32;
}
}
private void drawShapeOnForm()
{
Graphics g = this.CreateGraphics();
g.DrawRectangle(new Pen(Brushes.Blue), x, y, w, h);
g.FillRectangle(Brushes.Blue, x, y, w, h);
}
//Button draws square
private void COIN_Click(object sender, EventArgs e)
{
drawShapeOnForm();
}
private void Engine_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A)
{
x--;
}
}
private void button1_Click(object sender, EventArgs e)
{
running = true;
if (running)
{
PlayerCreator.Enabled = false;
EnemyCreator.Enabled = false;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
Invalidate();
}
}
}
My shape only appears for a few secs when I press the button to make it, I'm not to sure how to fix this.
When I press the button it appears and then disappears, but if the function is put on the void Engine_Paint() it will work just fine. Please help me fix this problem.
You need to do your drawing in the Paint handler, using e.Graphics. Do not use CreateGraphics.
From your button click, you can just call this.Invalidate(); to trigger a repaint.
Following is my code. i am trying to draw line, filled rectangle, etc.....
Problem is that, lets suppose i draw a line but when i try to draw an other line first drawn line disappears. so i want help that i'll be able to draw multiple shapes on a form and first draw lines don't disappears.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace finalPaint
{
public partial class Form1 : Form
{
List<Point> points = new List<Point>();
Rectangle rect;
Point first;
Point last;
string op;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
points.Add(e.Location);
rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y - rect.Top);
last = e.Location;
this.Invalidate();
this.Update();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
switch (op)
{
case "toolStripButton1":
{
if (points.Count > 2)
{
e.Graphics.DrawLines(Pens.Black, points.ToArray());
}
}
break;
case "toolStripButton2":
{
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, rect);
}
}
break;
case "toolStripButton3":
{
Pen pen = new Pen(Color.Red, 2);
e.Graphics.DrawLine(pen, first, last);
this.Update();
}
break;
case "toolStripButton4":
{
using (SolidBrush pen = new SolidBrush(Color.Red))
{
e.Graphics.FillRectangle(pen, rect);
}
}
break;
case "toolStripButton5":
{
using (SolidBrush pen = new SolidBrush(Color.Red))
{
e.Graphics.FillEllipse(pen, rect);
}
}
break;
case "toolStripButton6":
{
using (Pen pen = new Pen(Color.Red,2))
{
e.Graphics.DrawEllipse(pen, rect);
}
}
break;
default:
break;
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
rect = new Rectangle(e.X, e.Y, 0, 0);
first = e.Location;
this.Invalidate();
}
private void toolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
}
private void selectedButton(object sender, EventArgs e)
{
foreach (ToolStripButton btn in toolStrip1.Items)
{
btn.Checked = false;
}
ToolStripButton btnClicked = sender as ToolStripButton;
btnClicked.Checked = true;
op = btnClicked.Name;
}
}
}
On each Paint event, you need to paint all of the objects you want on the screen. You are not just painting on top of what is already there. You are repainting the entire scene.
The reason for this is that your control may be obscured from view at some point, and Windows will repaint it when it is revealed again.
If you want to keep a memory of all the the objects, you need to store them in your code. Since each object is different (lines, rectangles, ellipses) you will want to store them in a manner that lets you differentiate. You could create classes like this:
public class DrawingShape
{
public string Name { get; set; }
public DrawingShapeType Type { get; set; }
// other shared properties
public virtual void Draw(Graphics g)
{
}
}
public class DrawingRectangle : DrawingShape
{
public DrawingRectangle()
{
Name = "Rectangle";
Type = DrawingShapeType.Rectangle;
}
public override void Draw(Graphics g)
{
// draw this shape
}
}
public enum DrawingShapeType
{
Rectangle,
Ellipse,
Line,
}
Then you can just store all your objects in a List. The order of the items in the list is your z-order, so you add items to the list and you enumerate through the list in your Paint event and draw each one differently depending on the type.
From here you can store pen and brush information in the class and other info. Your Paint event can tell each class to paint itself and it doesn't need to know which type they are.
You need a possibility to store all your shapes, in order to draw them all in the Paint event, since the background of the form is repainted before each call to Paint. Let us define a base class from which we will derive all the shapes
abstract class Shape
{
public abstract void Paint(PaintEventArgs e);
public abstract void UpdateShape(Point newLocation);
}
Here we declare the abstract methods Paint and UpdateShape that we will have to override in the derived classes. UpdateShape will be called in MouseMove.
Let us start with the freeform line
class FreeformLine : Shape
{
private List<Point> _points = new List<Point>();
public FreeformLine(Point startLocation)
{
_points.Add(startLocation);
}
public override void Paint(PaintEventArgs e)
{
if (_points.Count >= 2) {
e.Graphics.DrawLines(Pens.Black, _points.ToArray());
}
}
public override void UpdateShape(Point newLocation)
{
const int minDist = 3;
// Add new point only if it has a minimal distance from the last one.
// This creates a smoother line.
Point last = _points[_points.Count - 1];
if (Math.Abs(newLocation.X - last.X) >= minDist ||
Math.Abs(newLocation.Y - last.Y) >= minDist)
{
_points.Add(newLocation);
}
}
}
Here we need a list of points. In the constructor, we pass the first point. The Paint method just executes the paint logic that you had already defined and the UpdateShape method adds new points to our points list.
The straight line works in a very similar way, but defines only the first and the last point.
class StraightLine : Shape
{
private Point _first;
private Point _last;
public StraightLine(Point startLocation)
{
_first = startLocation;
}
public override void Paint(PaintEventArgs e)
{
if (!_last.IsEmpty) {
Pen pen2 = new Pen(Color.Red, 2);
e.Graphics.DrawLine(pen2, _first, _last);
}
}
public override void UpdateShape(Point newLocation)
{
_last = newLocation;
}
}
We define only one rectangle class and add a variable in order to remember if the shape is filled or not.
class RectangleShape : Shape
{
protected bool _filled;
protected Rectangle _rect;
protected Point _start;
public RectangleShape(Point startLocation, bool filled)
{
_start = startLocation;
_rect = new Rectangle(startLocation.X, startLocation.Y, 0, 0);
_filled = filled;
}
public override void Paint(PaintEventArgs e)
{
if (_filled) {
using (SolidBrush brush = new SolidBrush(Color.Red)) {
e.Graphics.FillRectangle(brush, _rect);
}
} else {
using (Pen pen = new Pen(Color.Red, 2)) {
e.Graphics.DrawRectangle(pen, _rect);
}
}
}
public override void UpdateShape(Point newLocation)
{
int x = Math.Min(_start.X, newLocation.X);
int y = Math.Min(_start.Y, newLocation.Y);
int width = Math.Abs(newLocation.X - _start.X);
int height = Math.Abs(newLocation.Y - _start.Y);
_rect = new Rectangle(x, y, width, height);
}
}
Finally, we declare the ellipse class. Since this one uses a rectangle as well, we just derive it from our rectangle class.
class Ellipse : RectangleShape
{
public Ellipse(Point startLocation, bool filled)
: base(startLocation, filled)
{
}
public override void Paint(PaintEventArgs e)
{
if (_filled) {
using (SolidBrush brush = new SolidBrush(Color.Red)) {
e.Graphics.FillEllipse(brush, _rect);
}
} else {
using (Pen pen = new Pen(Color.Red, 2)) {
e.Graphics.DrawEllipse(pen, _rect);
}
}
}
}
Here we only override the Paint method. All the rectangle update logic remains the same.
Now to the form. Here we declare the global variables
List<Shape> _shapes = new List<Shape>();
Shape _lastShape;
string op;
In the mouse down event we create a new shape and add it to the list like this
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
switch (op) {
case "toolStripButton1":
_lastShape = new FreeformLine(e.Location);
break;
case "toolStripButton2":
_lastShape = new RectangleShape(e.Location, false);
break;
case "toolStripButton3":
_lastShape = new StraightLine(e.Location);
break;
case "toolStripButton4":
_lastShape = new RectangleShape(e.Location, true);
break;
case "toolStripButton5":
_lastShape = new Ellipse(e.Location, true);
break;
case "toolStripButton6":
_lastShape = new Ellipse(e.Location, false);
break;
default:
break;
}
_shapes.Add(_lastShape);
Refresh();
}
In the MouseMove we update the last shape like this
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && _lastShape != null) {
_lastShape.UpdateShape(e.Location);
this.Refresh();
}
}
The Paint method is now much simpler
private void Form1_Paint(object sender, PaintEventArgs e)
{
foreach (Shape shape in _shapes) {
shape.Paint(e);
}
}
Note that we do all the shape specific things in the shape classes, instead of doing them in the form. The only place in the form where have to care about the different shapes, is where we create the different shapes. This is a typical object-oriented approach. It is easier to maintain and to extend. You could add new shapes, with only a minimum changes in the form itself.