Transferring graphics between threads using semaphores and buffers in C# - c#

My english isn't very good, its very simple written so sorry in advance...
I have seen similar questions to this but not using graphics.
What I am trying to do is set up a simple circuit display, where a dot starts at the ButtonPanel, which whilst the button is clicked and green, will release more dots, I have used semaphores and buffers to control whether it goes into the next panel, the idea is the dot will do a circuit around the panels (in a clockwise motion) until the (for now) hard-coded limit makes the origin stop sending out dots.
I am confused with the nature of buffers and semaphores I guess, but as much reading as I have done on them, I still fail to get it to behave how I want...
The system when it leaves the first panel, will set off a dot in all 4 WaitPanelThreads at the same time, which is not what I want.
I have tried just adding more semaphores but this doesn't do any good. I built my own semaphores and buffers to try and understand what they are doing but I am unable to proceed with this program. I have spent ages trying to get the system to behave correctly but I am stumped, if any of this didn't make much sense because of my english, ask and I will try to phrase it better.
Below is all the code I am written for this, I am pretty sure the problem is where I construct my WaitPanelThreads. Any assistance is appreciated, I'm trying to teach myself this.
Felix
EDIT: I must also comment that the below code will all work if you slam it into visual studio, you may need to include two references if you want to see how it is set up
using System;
using System.Windows.Forms;
using System.Threading;
using System.ComponentModel;
using System.Collections;
using System.Data;
using System.Drawing;
public class Form1 : Form
{
private Container components = null;
private ButtonPanelThread p1;
private Button btn1;
private WaitPanelThread p2, p3, p4, p5;
private Thread thread1, thread2, thread3, thread4, thread5;
private Semaphore semaphore, semaphore1;
private Buffer buffer, buffer1;
private Thread semThread, semThread1;
private Thread buffThread, buffThread1;
private Panel pnl1, pnl2, pnl3, pnl4, pnl5;
public Form1()
{
InitializeComponent();
semaphore = new Semaphore();
semaphore1 = new Semaphore();
buffer = new Buffer();
buffer1 = new Buffer();
p1 = new ButtonPanelThread(new Point(40, 10),
30, true, pnl1,
Color.Blue,
semaphore,
buffer,
btn1);
p2 = new WaitPanelThread(new Point(10, 10),
80, false, 2, pnl2,
Color.White,
semaphore,
buffer);
p3 = new WaitPanelThread(new Point(10, 10),
77, false, 3, pnl3,
Color.White,
semaphore,
buffer);
p4 = new WaitPanelThread(new Point(250, 10),
77, false, 4, pnl4,
Color.White,
semaphore,
buffer);
p5 = new WaitPanelThread(new Point(10, 250),
77, false, 1, pnl5,
Color.White,
semaphore,
buffer);
semThread = new Thread(new ThreadStart(semaphore.Start));
semThread1 = new Thread(new ThreadStart(semaphore1.Start));
buffThread = new Thread(new ThreadStart(buffer.Start));
buffThread1 = new Thread(new ThreadStart(buffer1.Start));
thread1 = new Thread(new ThreadStart(p1.Start));
thread2 = new Thread(new ThreadStart(p2.Start));
thread3 = new Thread(new ThreadStart(p3.Start));
thread4 = new Thread(new ThreadStart(p4.Start));
thread5 = new Thread(new ThreadStart(p5.Start));
this.Closing += new CancelEventHandler(this.Form1_Closing);
semThread.Start();
semThread1.Start();
buffThread.Start();
buffThread1.Start();
thread1.Start();
thread2.Start();
thread3.Start();
thread4.Start();
thread5.Start();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.Text = "Circuit movements";
this.Size = new System.Drawing.Size(900, 700);
this.BackColor = Color.LightGray;
this.pnl1 = new Panel();
this.pnl1.Location = new Point(100, 100);
this.pnl1.Size = new Size(260, 30);
this.pnl1.BackColor = Color.White;
this.btn1 = new Button();
this.btn1.Size = new Size(30, 30);
this.btn1.BackColor = Color.Pink;
this.btn1.Location = new System.Drawing.Point(0, 0);
this.pnl2 = new Panel();
this.pnl2.Location = new Point(350, 100);
this.pnl2.Size = new Size(260, 30);
this.pnl2.BackColor = Color.White;
this.pnl3 = new Panel();
this.pnl3.Location = new Point(580, 100);
this.pnl3.Size = new Size(30, 260);
this.pnl3.BackColor = Color.White;
this.pnl4 = new Panel();
this.pnl4.Location = new Point(350, 360);
this.pnl4.Size = new Size(260, 30);
this.pnl4.BackColor = Color.White;
this.pnl5 = new Panel();
this.pnl5.Location = new Point(350, 100);
this.pnl5.Size = new Size(30, 260);
this.pnl5.BackColor = Color.White;
this.Controls.Add(pnl1);
this.Controls.Add(pnl2);
this.Controls.Add(pnl3);
this.Controls.Add(pnl4);
this.Controls.Add(pnl5);
this.pnl1.Controls.Add(btn1);
// Wire Closing event.
this.Closing += new CancelEventHandler(this.Form1_Closing);
}
private void Form1_Closing(object sender, CancelEventArgs e)
{
// Environment is a System class.
// Kill off all threads on exit.
Environment.Exit(Environment.ExitCode);
}
}// end class form1
public class Buffer
{
private Color planeColor;
private bool empty = true;
public void Read(ref Color planeColor)
{
lock (this)
{
// Check whether the buffer is empty.
if (empty)
Monitor.Wait(this);
empty = true;
planeColor = this.planeColor;
Monitor.Pulse(this);
}
}
public void Write(Color planeColor)
{
lock (this)
{
// Check whether the buffer is full.
if (!empty)
Monitor.Wait(this);
empty = false;
this.planeColor = planeColor;
Monitor.Pulse(this);
}
}
public void Start()
{
}
}// end class Buffer
public class Semaphore
{
private int count = 0;
public void Wait()
{
lock (this)
{
while (count == 0)
Monitor.Wait(this);
count = 0;
}
}
public void Signal()
{
lock (this)
{
count = 1;
Monitor.Pulse(this);
}
}
public void Start()
{
}
}// end class Semaphore
public class ButtonPanelThread
{
private Point origin;
private int delay;
private Panel panel;
private bool westEast;
private Color colour;
private Point plane;
private int xDelta;
private int yDelta;
private Semaphore semaphore;
private Buffer buffer;
private Button btn;
private bool locked = true;
public ButtonPanelThread(Point origin,
int delay,
bool westEast,
Panel panel,
Color colour,
Semaphore semaphore,
Buffer buffer,
Button btn)
{
this.origin = origin;
this.delay = delay;
this.westEast = westEast;
this.panel = panel;
this.colour = colour;
this.plane = origin;
this.panel.Paint += new PaintEventHandler(this.panel_Paint);
this.xDelta = westEast ? +10 : -10;
this.yDelta = 0;
this.semaphore = semaphore;
this.buffer = buffer;
this.btn = btn;
this.btn.Click += new System.
EventHandler(this.btn_Click);
}
private void btn_Click(object sender,
System.EventArgs e)
{
locked = !locked;
this.btn.BackColor = locked ? Color.Pink : Color.LightGreen;
lock (this)
{
if (!locked)
Monitor.Pulse(this);
}
}
public void Start()
{
Color signal = Color.Red;
Thread.Sleep(delay);
for (int k = 1; k <= 30; k++)
{
this.zeroPlane();
panel.Invalidate();
lock (this)
{
while (locked)
{
Monitor.Wait(this);
}
}
for (int i = 1; i <= 20; i++)
{
this.movePlane(xDelta, yDelta);
Thread.Sleep(delay);
panel.Invalidate();
}
semaphore.Wait();
buffer.Write(this.colour);
}
this.colour = Color.Gray;
panel.Invalidate();
}
private void zeroPlane()
{
plane.X = origin.X;
plane.Y = origin.Y;
}
private void movePlane(int xDelta, int yDelta)
{
plane.X += xDelta; plane.Y += yDelta;
}
private void panel_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
SolidBrush brush = new SolidBrush(colour);
g.FillRectangle(brush, plane.X, plane.Y, 10, 10);
brush.Dispose(); // Dispose graphics resources.
g.Dispose(); //
}
}// end class ButtonPanelThread
public class WaitPanelThread
{
private Point origin;
private int delay;
private Panel panel;
private bool westEast;
private int dir;
/*
* dir options:
* 1 - north;
* 2 - east;
* 3 - south;
* 4 - west.
*/
private Color colour;
private Point plane;
private int xDelta;
private int yDelta;
private Semaphore semaphore;
private Buffer buffer;
public WaitPanelThread(Point origin,
int delay,
bool westEast,
int dir,
Panel panel,
Color colour,
Semaphore sem,
Buffer buff)
{
this.origin = origin;
this.delay = delay;
this.westEast = westEast;
this.dir = dir;
this.panel = panel;
this.colour = colour;
this.plane = origin;
this.panel.Paint += new PaintEventHandler(this.panel_Paint);
switch (dir)
{
case 1:
this.xDelta = 0;
this.yDelta = -10;
break;
case 2:
this.xDelta = +10;
this.yDelta = 0;
break;
case 3:
this.xDelta = 0;
this.yDelta = +10;
break;
case 4:
this.xDelta = -10;
this.yDelta = 0;
break;
}
this.semaphore = sem;
this.buffer = buff;
}
public void Start()
{
this.colour = Color.White;
for (int k = 1; k <= 40; k++)
{
semaphore.Signal();
this.zeroPlane();
buffer.Read(ref this.colour);
for (int i = 1; i <= 20; i++)
{
panel.Invalidate();
this.movePlane(xDelta, yDelta);
Thread.Sleep(delay);
}
this.colour = Color.White;
panel.Invalidate();
}
this.colour = Color.Gray;
panel.Invalidate();
}
private void zeroPlane()
{
plane.X = origin.X;
plane.Y = origin.Y;
}
private void movePlane(int xDelta, int yDelta)
{
plane.X += xDelta; plane.Y += yDelta;
}
private void panel_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
SolidBrush brush = new SolidBrush(colour);
if (dir == 2 || dir == 4)
{
g.FillRectangle(brush, plane.X, plane.Y, 10, 10);
}
if (dir == 1 || dir == 3)
{
g.FillRectangle(brush, plane.X, plane.Y, 10, 10);
}
brush.Dispose(); // Dispose graphics resources.
g.Dispose(); //
}
}// end class WaitPanelThread
public class Driver
{
public static void Main()//
{
Application.Run(new Form1());
}
}// end class TheOne

Related

Use a KeyDown inside a Timer_Tick Event

Me and my buddy have been working on this magnifier application and we cannot make it work the way we want it.
The way we would like it to work:
Open app.
Move mouse to area you want magnified.
Hit enter.
Magnifying window moves to (offset) location of mouse and keeps updating that window for that specific location.
Hit enter again to move window to new cursor location.
Right now once i hit enter, the window follows the mouse because it goes into a for loop where it grabs "Cursor.Position". I've tried to save the Cursor.Position value at the "OnkeyDown" event and use it inside the timer loop but that won't work since it "does not exist in current context".
Can anyone see how i can do this?
Thanks in advance!
/Morten
/* O-button zooms out
* I-button zooms in
* Esc-button exits app
* Enter moves magnifying window to new location (doesn't work)
*/
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Magnifier
{
public partial class Form1 : Form
{
Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
PictureBox pictureBox1 = new PictureBox();
int zoom = 3; //zoom level
public bool NewZoomLocation = false;
public Form1()
{
{
InitializeComponent();
pictureBox1.Dock = DockStyle.Fill;
pictureBox1.BorderStyle = BorderStyle.FixedSingle;
Controls.Add(pictureBox1);
FormBorderStyle = FormBorderStyle.None;
Timer timer = new Timer();
timer.Interval = 100;
timer.Tick += timer_Tick;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{ var position = Cursor.Position;
int xlocation = position.X;
int ylocation = position.Y;
{
try
{
var graphics = Graphics.FromImage(printscreen as Image);
graphics.CopyFromScreen(0, 0, 0, 0, printscreen.Size);
GC.Collect(); // Force the garbage collector (deals with memory leak)
if (NewZoomLocation == true)
{
var lensbmp = new Bitmap(50, 50); //Bitmap for Zoom window
var i = 0;
var j = 0;
for (int row = xlocation - 25; row < xlocation + 25; row++)
{
j = 0;
for (int column = ylocation - 25; column < ylocation + 25; column++)
{
lensbmp.SetPixel(i, j, printscreen.GetPixel(row, column));
j++;
}
i++;
}
this.pictureBox1.Image = new Bitmap(lensbmp, lensbmp.Width * zoom, lensbmp.Height * zoom);
Size = pictureBox1.Image.Size;
Left = xlocation - 45 * (zoom); //Horisontal position of final zoom window
Top = ylocation + 30; //Vertical position of final zoom window
TopMost = true;
}
}
catch //(Exception ex)
{
//MessageBox.Show(ex.Message);
}
}
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyValue == 73) // I-button to zoom in
zoom++;
else if (e.KeyValue == 79) // O-button to zoom in
zoom--;
else if (e.KeyValue == 27) // Esc-button to exit
{
Close();
Dispose();
}
else if (e.KeyValue == 13) // Enter-button to choose zoon area
{
NewZoomLocation = true;
}
base.OnKeyDown(e);
}
}
}
I'm not really sure what you want to achieve here, however this should get you in a better place.
First thing first. The use of GC.Collect its because you are trying to plug a memory leak, if you ever create an image, dispose of it.
Given some globals
private readonly PictureBox pictureBox1 = new PictureBox();
private Bitmap _lastBmp = new Bitmap(300, 300);
private Point _position;
public bool NewZoomLocation;
private int zoom = 3; //zoom level
Constructor
public Form1()
{
InitializeComponent();
pictureBox1.Dock = DockStyle.Fill;
pictureBox1.BorderStyle = BorderStyle.FixedSingle;
Controls.Add(pictureBox1);
FormBorderStyle = FormBorderStyle.None;
KeyPreview = true;
Size = _lastBmp.Size;
TopMost = true;
var timer = new Timer();
timer.Interval = 100;
timer.Tick += timer_Tick;
timer.Start();
}
Cleanup
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
_lastBmp.Dispose();
_lastBmp = null;
}
Keydown
protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
base.OnPreviewKeyDown(e);
switch (e.KeyCode)
{
case Keys.Enter:
NewZoomLocation = true;
_position = Cursor.Position;
break;
case Keys.Up:
zoom++;
break;
case Keys.Down:
zoom--;
break;
case Keys.Escape:
Close();
break;
}
}
Timer
private void timer_Tick(object sender, EventArgs e)
{
if (NewZoomLocation)
{
var w = _lastBmp.Size.Width / zoom;
var h = _lastBmp.Size.Height / zoom;
var x = _position.X - w / 2;
var y = _position.Y - h / 2;
var size = new Size(w, h);
using (var screen = new Bitmap(size.Width, size.Height))
{
using (var g = Graphics.FromImage(screen))
{
g.CopyFromScreen(new Point(x, y), Point.Empty, size);
}
// resize
using (var g = Graphics.FromImage(_lastBmp))
{
g.DrawImage(screen, new Rectangle(new Point(), _lastBmp.Size), new Rectangle(0, 0, w, h), GraphicsUnit.Pixel);
}
}
pictureBox1.Image = _lastBmp;
}
}
There is a lot more that can be done with this, however it should get you started. There is no memory leak anymore, it only grabs a screen shot of what it needs so will be faster.
Good luck

Graphical Timer control not working correctly

This is a custom control I have made, a graphical timer, however, it is not working correctly. As the time left decreases a pie is filled to represent the amount of time left decreasing, but it is decreasing with unexpected angles (which are output to the listbox for debugging purposes). How do I get it to work correctly? Also I'm very new to making Custom Controls (this is my first) so any pointers on good coding guidelines, what not to do, etc, would be very helpful.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TestCustomControl
{
class GraphicalTimer : Control
{
public Color Timer { get; set; }
public Color TimerEmpty { get; set; }
public Color BorderColor { get; set; }
private Timer t;
public int MaxTime { get; set; }
private int timeElapsed = 0;
public GraphicalTimer()
{
DoubleBuffered = true;
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
t = new Timer();
t.Interval = 1000;
t.Tick += t_Tick;
}
public void Start()
{
t.Start();
}
public void Stop()
{
t.Stop();
}
public void Reset()
{
timeElapsed = 0;
Invalidate();
}
void t_Tick(object sender, EventArgs e)
{
timeElapsed += 1;
if (timeElapsed == MaxTime)
{
t.Dispose();
}
Invalidate();
}
private float getAngleFromTime()
{
if (timeElapsed == 0)
{
return 0;
}
else
{
MainWindow.lb.Items.Add((360 / (MaxTime / timeElapsed)).ToString());
return (360 / (MaxTime / timeElapsed));
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Rectangle rc = ClientRectangle;
g.FillEllipse(new SolidBrush(Timer), rc);
g.FillPie(new SolidBrush(TimerEmpty), rc, -90, getAngleFromTime());
g.DrawEllipse(new Pen(BorderColor, 4), rc);
Font font = new Font("Arial", (float)rc.Height * 0.4f, FontStyle.Bold, GraphicsUnit.Pixel);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString((MaxTime - timeElapsed).ToString("D2"), font, new SolidBrush(Color.Black), new RectangleF((rc.Width - rc.Width / 2) / 2, (rc.Height - rc.Height / 2) / 2, rc.Width * 0.7f, rc.Height * 0.7f));
}
}
class MainWindow : Form
{
GraphicalTimer gt;
Button startButton;
Button stopButton;
Button resetButton;
public static ListBox lb;
public MainWindow()
{
this.Text = "Test Application";
gt = new GraphicalTimer();
gt.MaxTime = 20;
gt.BorderColor = Color.BurlyWood;
gt.Timer = Color.Aqua;
gt.TimerEmpty = Color.White;
gt.Top = 10;
gt.Left = 10;
gt.Width = 50;
gt.Height = 50;
this.Controls.Add(gt);
startButton = new Button();
startButton.Top = 70;
startButton.Left = 30;
startButton.AutoSize = true;
startButton.Text = "Start Timer";
startButton.Click += startButton_Click;
this.Controls.Add(startButton);
stopButton = new Button();
stopButton.Top = 70;
stopButton.Left = startButton.Right + 10;
stopButton.AutoSize = true;
stopButton.Text = "Stop Timer";
stopButton.Click += stopButton_Click;
this.Controls.Add(stopButton);
resetButton = new Button();
resetButton.Top = 70;
resetButton.Left = stopButton.Right + 10;
resetButton.AutoSize = true;
resetButton.Text = "Reset Timer";
resetButton.Click += resetButton_Click;
this.Controls.Add(resetButton);
lb = new ListBox();
lb.Top = resetButton.Bottom + 10;
lb.Left = 10;
lb.Width = this.ClientSize.Width - 20;
lb.Height = this.ClientSize.Height - lb.Top - 10;
this.Controls.Add(lb);
}
void resetButton_Click(object sender, EventArgs e)
{
gt.Reset();
}
void stopButton_Click(object sender, EventArgs e)
{
gt.Stop();
}
void startButton_Click(object sender, EventArgs e)
{
gt.Start();
}
}
class StartClass
{
static void Main()
{
MainWindow form = new MainWindow();
Application.EnableVisualStyles();
Application.Run(form);
}
}
}
You are using integers to calculate the angle, and in a way that can cause quite some jitters.
360 / (MaxTime / timeElapsed) will first evaluate temp = MaxTime / timeElapsed in integer arithmetics, and then 360 / temp also using integer division.
Try using floating point numbers for the calculation and then converting the final result into an integer value if you need it that way. Even writing 360 * timeElapsed / MaxTime might reduce the artifacts as you then first multiply 360 * timeElapsed which is accurate (unless timeElapsed is very large).
The method of calculating AngleForTime is wrong. Just replace your method with below code and it should do your job.
;)
private float getAngleFromTime()
{
if (timeElapsed == 0)
{
return 0;
}
else
{
MainWindow.lb.Items.Add((360*timeElapsed) / MaxTime ).ToString();
return (360*timeElapsed) / MaxTime ;
}
}

C# picturebox and timer

I am trying to create a car game using pictureboxes and timers
Here is my code
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
PictureBox car = new PictureBox();
Timer t = new Timer();
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
//Right arrow key
if (e.KeyCode.Equals(Keys.Right) && carPlayer.Location.X < 300)
{
int x = carPlayer.Location.X + 5;
int y = carPlayer.Location.Y;
int width = carPlayer.Size.Width;
int height = carPlayer.Size.Height;
carPlayer.SetBounds(x, y, width, height);
}
//Left arrow key
if (e.KeyCode.Equals(Keys.Left) && carPlayer.Location.X > 35)
{
int x = carPlayer.Location.X - 5;
int y = carPlayer.Location.Y;
int width = carPlayer.Size.Width;
int height = carPlayer.Size.Height;
carPlayer.SetBounds(x, y, width, height);
}
if (e.KeyCode.Equals(Keys.Space))
{
spawnCar();
}
}
void spawnCar()
{
string[] cars = { "data/car_red.png", "data/car_blue.png", "data/car_green.png", "data/car_grey.png" };
Random rand = new Random();
car.SizeMode = PictureBoxSizeMode.StretchImage;
car.Image = Image.FromFile(cars[rand.Next(0, 4)]);
car.Visible = true;
if (rand.Next(0,2) == 0)
{
car.SetBounds(100, 10, 50, 85);
}
else
{
car.SetBounds(250, 10, 50, 85);
}
this.Controls.Add(car);
car.BringToFront();
t.Interval = 1;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
private void t_Tick(object sender, EventArgs e)
{
if (car.Bounds.IntersectsWith(carPlayer.Bounds))
{
t.Stop();
car.Image = Image.FromFile("data/car_wreck.png");
carPlayer.Image = Image.FromFile("data/player_wreck.png");
}
if (car.Bounds.Y > 340)
{
t.Stop();
this.Controls.Remove(car);
}
else
{
car.Top++;
}
}
}
http://i.stack.imgur.com/NhCgY.png
Now when I press space once the car appears at the top and moves down slowly and disappears on reaching the bottom but when I press space multiple time the speed of the car gets faster and faster .
Anyone please help me make the car move at same speed everytime it is created.
Thanks
The problem is that you are registering a new Tick event handler with each car spawn, you only want to do this once. However, there isn't an easy way to check if a handler has been assigned yet so I would recommend using a global flag...
//at class level
bool eventSet = false;
//in spawn method
t.Interval = 1;
if(!eventSet)//check if no handler assigned yet
{
t.Tick += new EventHandler(t_Tick);
eventSet = true;
}
t.Start();
Alternatively, you could attempt to remove the handler before assigning...
//in spawn method
t.Interval = 1;
t.Tick -= new EventHandler(t_Tick);//remove previous one if it exists
t.Tick += new EventHandler(t_Tick);
t.Start();

How do I properly add a custom control to another custom control so that it will render in a form?

I've created a custom control in C#. A highly-simplified version of it is shown below:
class WellControl : Control
{
Pen circlePen = new Pen(Color.Black, 5);
Brush wellShader = new SolidBrush(Color.BlueViolet);
Brush wellBlanker = new SolidBrush(Color.LightGray);
public WellControl(int wellNum)
{
InitializeComponent();
}
private void DrawWell()
{
using (Graphics well = this.CreateGraphics())
{
this.Size = new Size(WellSize, WellSize);
if (this.selected)
{
well.FillEllipse(wellShader, ellipseCoords);
}
else
{
well.FillEllipse(wellBlanker, ellipseCoords);
}
well.DrawEllipse(circlePen, ellipseCoords);
using (Font wellNumberFont = new Font("Arial", 14, FontStyle.Bold))
{
well.DrawString(WellNum.ToString(), wellNumberFont, Brushes.Black, new Point(13, 13));
}
}
}
private void WellPaintEventHandler(object sender, EventArgs e)
{
DrawWell();
}
private void InitializeComponent()
{
this.SuspendLayout();
this.Paint += new System.Windows.Forms.PaintEventHandler(WellPaintEventHandler);
this.ResumeLayout();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
//dispose all the custom stuff
}
}
}
When I add it to a form, it renders properly (again, simplified example):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
for (int i = 0; i < 4; i++)
{
WellControl newWell = new WellControl(i + 1);
newWell.Location = new Point(15, 50 * i);
newWell.Size = new System.Drawing.Size(45, 45);
this.Controls.Add(newWell);
}
}
}
I have another custom control, "Plate", which is intended to hold many "Wells". The code intended to draw the wells evenly across the plate probably sucks right now but I'm just trying to see something:
class PlateControl : Control
{
Pen blackPen = new Pen(Color.Black, 3);
public PlateControl()
{
this.Size = new Size(600, 800);
List<WellControl> plateWells = new List<WellControl>();
int column = 1;
int row = 0;
for (int i = 1; i <= 96; i++)
{
column = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(i / 8)));
row = i % 8;
WellControl newWell = new WellControl(i + 1);
newWell.Name = "wellControl" + i;
newWell.Location = new Point(column * 50, row * 50);
newWell.Size = new System.Drawing.Size(45, 45);
newWell.TabIndex = i;
newWell.WellSize = 45;
plateWells.Add(newWell);
newWell.Visible = true;
}
InitializeComponent();
}
private void InitializeComponent()
{
this.SuspendLayout();
this.Paint += new System.Windows.Forms.PaintEventHandler(PlatePaintEventHandler);
this.ResumeLayout();
}
private void DrawPlate()
{
using (Graphics plate = this.CreateGraphics())
{
Point topLeft = new Point(0, 0);
Point topRight = new Point(600, 0);
Point bottomRight = new Point(600, 400);
Point bottomLeft = new Point(0, 400);
plate.DrawLine(blackPen, topLeft, topRight);
plate.DrawLine(blackPen, topRight, bottomRight);
plate.DrawLine(blackPen, bottomRight, bottomLeft);
plate.DrawLine(blackPen, bottomLeft, topLeft);
}
}
private void PlatePaintEventHandler(object sender, EventArgs e)
{
DrawPlate();
}
}
If I add this control to a Winform using this.Controls.Add(new PlateControl()), the rectangle renders, but not the WellControls I added to the PlateControl in the constructor loop. What am I doing incorrectly?
You need to add List of WallControl to plate control.
public PlateControl()
{
this.Size = new Size(600, 800);
List<WellControl> plateWells = new List<WellControl>();
int column = 1;
int row = 0;
for (int i = 1; i <= 96; i++)
{
column = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(i / 8)));
row = i % 8;
WellControl newWell = new WellControl(i + 1);
newWell.Name = "wellControl" + i;
newWell.Location = new Point(column * 50, row * 50);
newWell.Size = new System.Drawing.Size(45, 45);
newWell.TabIndex = i;
newWell.WellSize = 45;
plateWells.Add(newWell);
newWell.Visible = true;
}
this.Controls.AddRange(plateWells.ToArray());
InitializeComponent();
}

Moving drawn items

I am creating dynamic text fields and storing Position and font etc details in an Arraylist. So for example if I click 3 times on Form I am generating 3 random numbers and showing it on clicked position on form. I have one selector button when it is clicked then adding more text functionality is disabled.
Now after when selector button is clicked and if I click ( MouseDown event) on any text then it should move along mouse until MouseUp event is not fired and place on new drop position.
After hours of struggling and searching I found This Solution, So I saved the position and checked with IsPointOnLine method but still its not draggable.
Thank you for any help.
Update: managed to get foreach on place but its still not dragging the selected element.
Form1.Class
public partial class Form1 : Form
{
private Point mouseDownPosition = new Point(0, 0);
private Point mouseMovePosition = new Point(0, 0);
private int mousePressdDown;
IList drawnItemsList = new List<DrawingData>();
private bool dragging;
private bool isSelectorClicked; // if selector button is clicked
DrawingData draggingText;
Random rnd;
int clicked = 0;
public Form1()
{
InitializeComponent();
this.rnd = new Random();
this.isSelectorClicked = false;
dragging = false;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
mouseMovePosition = e.Location;
if (e.Button == MouseButtons.Left)
mousePressdDown = 1;
if (isSelectorClicked)
{
foreach (DrawingData a in drawnItemsList)
{
if (a.IsPointOnLine(e.Location, 5))
{
draggingText = a;
dragging = true;
}
}
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mouseDownPosition = e.Location;
if (dragging)
{
draggingText.cur = mouseDownPosition;
this.Invalidate();
}
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (mousePressdDown == 1 && isSelectorClicked == false)
{
label1.Text = "X: " + mouseMovePosition.X.ToString();
label2.Text = "Y: " + mouseMovePosition.Y.ToString();
this.Invalidate();
DrawingData a = new DrawingData(mouseMovePosition, mouseDownPosition, rnd.Next().ToString(), FontStyle.Bold, 30, "Candara", Brushes.Blue);
drawnItemsList.Add(a);
this.clicked++;
}
mousePressdDown = 0;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if(drawnItemsList.Count != 0)
{
foreach (DrawingData a in drawnItemsList)
{
draw(e.Graphics, a);
}
if (mousePressdDown != 0)
{
draw(e.Graphics, mouseDownPosition, mouseMovePosition, FontStyle.Bold, 30, "Candara", Brushes.Blue);
}
}
}
private void draw(Graphics e, Point mold, Point mcur, FontStyle fontWeight, int fontSize, string fontName, Brush fontColor)
{
Pen p = new Pen(Color.Black, 2);
using (Font useFont = new Font(fontName, fontSize, fontWeight))
{
string header2 = rnd.Next().ToString();
RectangleF header2Rect = new RectangleF();
int moldX = mold.X - 5;
int moldY = mold.Y;
header2Rect.Location = new Point(moldX, moldY);
header2Rect.Size = new Size(600, ((int)e.MeasureString(header2, useFont, 600, StringFormat.GenericTypographic).Height));
e.DrawString(header2, useFont, fontColor, header2Rect);
}
}
private void draw(Graphics e, DrawingData a)
{
Pen p = new Pen(Color.Black, 2);
using (Font useFont = new Font(a.FontName, a.FontSize, a.FontWeight))
{
string header2 = rnd.Next().ToString();
RectangleF header2Rect = new RectangleF();
int moldX = a.old.X - 5;
int moldY = a.old.Y;
header2Rect.Location = new Point(moldX, moldY);
header2Rect.Size = new Size(600, ((int)e.MeasureString(header2, useFont, 600, StringFormat.GenericTypographic).Height));
e.DrawString(a.Rand, useFont, a.FontColor, header2Rect);
}
}
private void Select_button_Click(object sender, EventArgs e)
{
this.isSelectorClicked = true;
}
private void WriteNewText_button_Click(object sender, EventArgs e)
{
this.isSelectorClicked = false;
}
}
DrawingData.Class
[Serializable]
public class DrawingData
{
private Point Start; // mouseDown position
private Point End; // mouseUp poslition
private string randValue; // random data value
private FontStyle fontWeight;
private int fontSize;
private string fontName;
private Brush fontColor;
public DrawingData()
{
Start = new Point(0, 0);
End = new Point(0, 0);
randValue = String.Empty;
fontWeight = FontStyle.Bold;
}
public DrawingData(Point old, Point cur, string rand, FontStyle fs, int fSize, string fName, Brush fColor)
{
Start = old;
End = cur;
randValue = rand;
fontWeight = fs;
fontSize = fSize;
fontName = fName;
fontColor = fColor;
}
public float slope
{
get
{
return (((float)End.Y - (float)Start.Y) / ((float)End.X - (float)Start.X));
}
}
public float YIntercept
{
get
{
return Start.Y - slope * Start.X;
}
}
public bool IsPointOnLine(Point p, int cushion)
{
float temp = (slope * p.X + YIntercept);
if (temp >= (p.Y - cushion) && temp <= (p.Y + cushion))
{
return true;
}
else
{
return false;
}
}
public FontStyle FontWeight
{
get
{
return fontWeight;
}
set
{
fontWeight = value;
}
}
public int FontSize
{
get
{
return fontSize;
}
set
{
fontSize = value;
}
}
public string FontName
{
get
{
return fontName;
}
set
{
fontName = value;
}
}
public Brush FontColor
{
get
{
return fontColor;
}
set
{
fontColor = value;
}
}
public Point old
{
get
{
return Start;
}
set
{
Start = value;
}
}
public Point cur
{
get
{
return End;
}
set
{
End = value;
}
}
public string Rand
{
get
{
return randValue;
}
set
{
randValue = value;
}
}
}
SOLVED: I was missing these lines in my mouse move method:
m.Start = new Point(deltaStart.X + e.Location.X, deltaStart.Y + e.Location.Y);
m.End = new Point(deltaEnd.X + e.Location.X, deltaEnd.Y + e.Location.Y);
You should move stuff in Form1_MouseMove instead of Form1_MouseUp.
Find the element you want to move in the Arraylist and assign it new position.
Redraw the element.
Also you should use IList<DrawingData> instead of ArrayList

Categories