Problem
I'm using a C# application to record keyboard and mouse movement, processing that movement and sending serial data out to a micro-controller that interpreters that data and moves a set of servos. In the past I had created a box or image that was the resolution(number of steps) my servos were capable of, clipped the mouse to that box or image, and processed where the cursor was in that box and sent data to my servos to move to that position.
This worked fine and dandy till I needed to move a greater amount of steps than my monitor has resolution.
So my question is what options are available to me for tracking mouse movement up to 10,000 steps/resolution in the X and Y axis?
Possible solution route
Thinking outside the box I think I could hide and center the mouse on the screen, record how much the mouse moved on mousemoved events, process that data, then recenter the mouse on the screen to give me unlimited movement in each axis.
Enclosed below is my PIC18F2420 code. Currently it is fed x and y positions via serial communications from my C# application. Data is stored in a ring buffer as it is received and processed as soon as possible.
PIC CODE
#include <p18f2420.h>
#include <cType.h>
#include <usart.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <timers.h>
#include <delays.h>
/**********************************************************************************************/
//DEFINES
#define switch_0 PORTCbits.RC4
#define switch_1 PORTCbits.RC5
#define bufferSize 48
/**********************************************************************************************/
/**********************************************************************************************/
//Function prototypes
void high_isr(void);
void int2ASCII(unsigned int output);
void UART_putChar(unsigned char value);
char readBuffer();
char emptyBuffer();
char peekBuffer();
void limitServo0(); //limit movement via predetermined min/max
void limitServo1();
/**********************************************************************************************/
unsigned char hertz = 75; //value to generate 5-=60 hertz wave default value 75
unsigned int timer0, servo0Min, servo0Max;
unsigned int timer1, servo1Min, servo1Max;
unsigned char servo0Rate = 10;
unsigned char ByteOut;
char array[bufferSize]; //input rs-232 buffer
char valueArray[bufferSize];
char dataArray[bufferSize];
char tempArray[bufferSize];
unsigned char tempIndex;
unsigned char head = 0;
unsigned char tail = 0;
//variables used to disect the comma delimited string
char CVdata; //do we have a command and value?
char CVvalue; //bool value like above
//BOOLEAN IF values
/**********************************************************************************************/
//Interrupt Service Routine
#pragma code high_vector=0x08
void interrupt_at_high_vector (void)
{
_asm GOTO high_isr _endasm
}
#pragma code /* return to the default code section */
#pragma interrupt high_isr
void high_isr (void)
{
if(PIR1bits.TMR2IF == 1)
{
//T0CONbits.TMR0ON = 0;
//T1CONbits.TMR1ON = 0;
INTCONbits.TMR0IF = 0; //Turn off Int Flag
PIR1bits.TMR1IF = 0;
PIR1bits.TMR2IF = 0; //Turn off Int Flag
LATCbits.LATC3 = 1; //Turn on data line
TMR0H = timer0/256; //Extract HIGH byte always do Hbyte first
TMR0L = timer0; //Extract LOW byte
}
if(PIR1bits.TMR1IF == 1)
{
PIR1bits.TMR1IF = 0;
//T1CONbits.TMR1ON = 0;
//PIR1bits.TMR2IF = 0; //Turn off Int Flag
INTCONbits.TMR0IF = 0; //Turn off Int Flag
LATCbits.LATC2 = 0;
PR2 = hertz; //Generate 50-60hertz pulse
}
if(INTCONbits.TMR0IF == 1)
{
LATCbits.LATC2 = 1;
//PIR1bits.TMR1IF = 0;
//PIR1bits.TMR2IF = 0; //Turn off Int Flag
//T0CONbits.TMR0ON = 0;
//T1CONbits.TMR1ON = 1;
INTCONbits.TMR0IF = 0; //Turn off Int Flag
LATCbits.LATC3 = 0;
TMR1H = timer1/256;
TMR1L = timer1;
}
if(PIR1bits.RCIF == 1)
{
PIR1bits.RCIF = 0;
array[tail] = RCREG;
//array[tail] = ReadUSART();
tail++;
if(tail == bufferSize)
{
tail = 0;
}
/* Clear the interrupt flag */
}
}
/**********************************************************************************************/
void main(void)
{
/**********************************************************************************************/
//Initialize
memset(array, '\0' , bufferSize);
memset(tempArray, '\0' , bufferSize);
memset(dataArray, '\0' , bufferSize);
memset(valueArray, '\0' , bufferSize);
TRISC = 0b10110000;//RC4 and RC5 inputs for switches
servo0Max = 65000; //Max value allowed PAN 65000
servo0Min = 62000; //Min value allowed 63500
servo1Max = 65000; //Tilt 64138
servo1Min = 62000; //TILT 63864
timer0 = 64250; //Initial position
timer1 = 64200;
CVdata = 0;
CVvalue = 0;
tempIndex = 0;
LATCbits.LATC0 = 0;
/**********************************************************************************************/
//USART
OpenUSART(USART_TX_INT_OFF &
USART_RX_INT_ON &
USART_CONT_RX &
USART_ASYNCH_MODE &
USART_EIGHT_BIT &
USART_BRGH_HIGH
, 16);//change back to 16 for 57.6 103 for 9.6
RCSTAbits.ADDEN = 0;//Testing this out might not help with overflow
TXSTAbits.SYNC = 0;
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
TXSTAbits.BRGH = 1;
BAUDCONbits.BRG16 = 1;
/**********************************************************************************************/
//Initialize Timer0
OpenTimer0(TIMER_INT_ON &
T0_SOURCE_INT &
T0_16BIT);
T0CONbits.PSA = 1;
INTCONbits.TMR0IF = 0;
/**********************************************************************************************/
/**********************************************************************************************/
//Initialize Timer1
OpenTimer1(TIMER_INT_ON &
T1_16BIT_RW &
T1_SOURCE_INT &
T1_PS_1_1);
T1CONbits.T1CKPS1 = 0; // bits 5-4 Prescaler Rate Select bits
T1CONbits.T1CKPS0 = 0; // bit 4
T1CONbits.T1OSCEN = 1; // bit 3 Timer1 Oscillator Enable Control bit 1 = on
T1CONbits.T1SYNC = 1; // bit 2 Timer1 External Clock Input Synchronization Control bit...1 = Do not synchronize external clock input
T1CONbits.TMR1CS = 0; // bit 1 Timer1 Clock Source Select bit...0 = Internal clock (FOSC/4)
T1CONbits.TMR1ON = 1; // bit 0 enables timer
/**********************************************************************************************/
/**********************************************************************************************/
//Initialize Timer2
OpenTimer2( TIMER_INT_ON &
T2_PS_1_16 &
T2_POST_1_16);
PR2 = hertz;
PIE1bits.TMR2IE = 1;
IPR1bits.TMR2IP = 1;
INTCONbits.GIEH = 1; //enable global interrupts
INTCONbits.GIEL = 1;
/**********************************************************************************************/
while(1)
{
while(emptyBuffer());
if(CVdata == 0 && CVvalue == 1)
{
//ERROR THIS SHOULDN't HAPPEN! FLUSH BUFFER
CVdata = 0;
CVvalue = 0;
}
if(CVdata == 0 && CVvalue == 0)
{
if(peekBuffer() != ',')
{
tempArray[tempIndex] = readBuffer();
tempIndex++;
}
else
{
readBuffer();//if comma sent first read it and throw away
if(tempIndex > 0) //comma read and data in buffer
{
memcpy(dataArray, tempArray, tempIndex);
tempIndex = 0;
CVdata = 1;
memset(tempArray, 'a' , bufferSize);
}
}
}
if(CVdata ==1 && CVvalue == 0)
{
if(peekBuffer() != ',')
{
if(isdigit(peekBuffer()))
{
tempArray[tempIndex] = readBuffer();
tempIndex++;
}
else
readBuffer();
}
else
{
//readBuffer();
if(tempIndex > 0)
{
memcpy(valueArray, tempArray, tempIndex);
tempIndex = 0;
CVvalue = 1;
memset(tempArray, 'a', bufferSize);
}
}
}
if(CVdata == 1 && CVvalue == 1)
{
switch(dataArray[0])
{
case 'x':
case 'X':
{
//timer0 = current = atof(valueArray);//ISSUE HERE first char null
timer0 = (unsigned int)atoi(valueArray);
break;
}
case 'y':
case 'Y':
{
timer1 = (unsigned int)atoi(valueArray);
break;
}
}
CVdata = 0;
CVvalue = 0;
memset(dataArray, 'a' , bufferSize);
memset(valueArray, 'a' , bufferSize);
}
limitServo0();
limitServo1();
}
}
/**********************************************************************************************/
//Functions
void int2ASCII(unsigned int output)
{
unsigned char digit = 0;
while (output >= 10000) { output -= 10000; digit++; } UART_putChar(digit + 0x30); digit = 0;
while (output >= 1000) { output -= 1000; digit++; } UART_putChar(digit + 0x30); digit = 0;
while (output >= 100) { output -= 100; digit++; } UART_putChar(digit + 0x30); digit = 0;
while (output >= 10) { output -= 10; digit++; } UART_putChar(digit + 0x30); digit = 0;
while (output >= 1) { output -= 1; digit++; } UART_putChar(digit + 0x30);
}
void UART_putChar(unsigned char value)
{
while(PIR1bits.TXIF == 0);
TXREG = value;
}
char readBuffer()
{
if(tail != head)
{
ByteOut = array[head];
head++;
if(head == bufferSize)
{
head = 0;
}
return ByteOut;
}
//LATCbits.LATC0 = 1;
}
char peekBuffer()
{
return array[head];
}
char emptyBuffer()
{
if(tail == head)
return 1;
else
return 0;
}
void limitServo0()
{
if(timer0 > servo0Max)
{
timer0 = servo0Max;
}
if(timer0 < servo0Min)
{
timer0 = servo0Min;
}
}
void limitServo1()
{
if(timer1 > servo1Max)
{
timer1 = servo1Max;
}
if(timer1 < servo1Min)
{
timer1 = servo1Min;
}
}
An Example of my previous tracking via bitmap can be viewed on my youtube channel at: http://www.youtube.com/watch?v=rBhkV3dnyiU&list=UULGlw5rGZfETaiPs49JBEuA&index=41
UPDATE
After some research it seems I can write a XNA application, capture mouse movement, and output serial communications. I would really really like a windows forms solution but I do have XNA experience so guess I'll work on converting my application until another solution presents itself.
My initial purposed solution did the trick. On trackingEnabled I center the mouse and check how much it has moved every 100ms, after that I recenter it and send the data to my controller.
using System;
using System.Timers;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using PIC18F_Servo_Control_V2;
namespace FSI_Grid1
{
public partial class Form1 : Form
{
TabControl LeftControlTab;
System.Timers.Timer myTimer;
public delegate void UpdateStatusBarDelegate();
TabPage myTabPage;
Grid myGrid;
serialConnection myConnection;
int timerDelay = 100;
int initialX, initialY, currentX, currentY, minX, minY, maxX, maxY;
int MouseCurrentX, MouseCurrentY, MouseMovedX, MouseMovedY;//tracking mousemovement
int offsetX, offsetY;//how much each arrow click moves the servos
bool trackingActive;//are we in tracking mode?
bool MouseMovedFlag;
int YOffsetValue;//used to offset dynamically generated buttons in tab groups
int XCenter, YCenter;
enum States { Startup, MouseTracking, KeyboardTracking, Script, Idle };//state engine
States CurrentState;
public Form1()
{
currentX = initialX = 63503;
currentY = initialY = 64012;
minX = 62000;
maxX = 65000;
minY = 62000;
maxY = 65000;
offsetX = 10;
offsetY = 10;
trackingActive = false;
YOffsetValue = 0;
CurrentState = States.Startup;
MouseMovedFlag = false;
myTimer = new System.Timers.Timer(timerDelay);
myTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
myTimer.Elapsed += new ElapsedEventHandler(TrackMouse);
myTimer.Enabled = false;
InitializeComponent();
InitializeGrid();
InitializeLeftControlTab();
InitializeSerial();
//Initialize StatusBar
RadioButton button = (RadioButton)this.Controls.Find("SelectKeyboardRadioButton", true)[0];
button.Checked = true;
activeStatus.Text = "Keyboard Tracking DEACTIVATED";
activeStatus.BackColor = Color.Red;
ConnectionStatus.Text = "Disconnected!";
xOffsetStatus.Text = "X offset value " + offsetX.ToString();
yOffsetStatus.Text = "Y offset value " + offsetY.ToString();
//this.MouseMove += new MouseEventHandler(Form_MouseMove);
XCenter = this.Location.X + this.Width / 2;
YCenter = this.Location.Y + this.Height / 2;
}
~Form1()
{
if (myConnection.connected)
myConnection.disconnect();
}
private void widthTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
}
private void widthTextBox_KeyUp(object sender, KeyEventArgs e)
{
TextBox text = (TextBox)this.Controls.Find("widthTextBox", true)[0];
xOffsetStatus.Text = text.Text;
offsetX = Convert.ToInt16(text.Text);
}
private void heightTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
}
private void heightTextBox_KeyUp(object sender, KeyEventArgs e)
{
TextBox text = (TextBox)this.Controls.Find("heightTextBox", true)[0];
yOffsetStatus.Text = text.Text;
offsetY = Convert.ToInt16(text.Text);
}
private void LeftControlTab_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _textBrush;
// Get the item from the collection.
TabPage _tabPage = LeftControlTab.TabPages[e.Index];
// Get the real bounds for the tab rectangle.
Rectangle _tabBounds = LeftControlTab.GetTabRect(e.Index);
if (e.State == DrawItemState.Selected)
{
// Draw a different background color, and don't paint a focus rectangle.
_textBrush = new SolidBrush(Color.Red);
g.FillRectangle(Brushes.White, e.Bounds);
}
else
{
_textBrush = new System.Drawing.SolidBrush(e.ForeColor);
g.FillRectangle(Brushes.LightGray, e.Bounds);
//e.DrawBackground();
}
// Use our own font.
Font _tabFont = new Font("Arial", (float)10.0, FontStyle.Bold, GraphicsUnit.Pixel);
// Draw string. Center the text.
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Center;
_stringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_tabPage.Text, _tabFont, _textBrush, _tabBounds, new StringFormat(_stringFlags));
}
private void InitializeLeftControlTab()
{
LeftControlTab = new TabControl();
LeftControlTab.Location = new Point(10, 30);
LeftControlTab.Size = new Size(300, 500);
LeftControlTab.Alignment = TabAlignment.Left;
LeftControlTab.SizeMode = TabSizeMode.Fixed;
LeftControlTab.ItemSize = new Size(30, 90);
LeftControlTab.DrawMode = TabDrawMode.OwnerDrawFixed;
/*EVENT HANDLER*/
LeftControlTab.DrawItem += new DrawItemEventHandler(LeftControlTab_DrawItem);
/*TABS*/
int offset = 100; //how far to the right the edit boxes are
myTabPage = new TabPage();
myTabPage.Text = "Appearance";
LeftControlTab.Controls.Add(myTabPage);
myTabPage = new TabPage();
myTabPage.Text = "Settings";
/*LABEL*/
Label OffsetLabel = new Label();
OffsetLabel.Text = "Step resolution";
OffsetLabel.Location = new Point(0,YOffset());
myTabPage.Controls.Add(OffsetLabel);
/*WIDTH LABEL*/
Label widthLabel = new Label();
widthLabel.Text = "Width";
widthLabel.Location = new Point(0, YOffset());
myTabPage.Controls.Add(widthLabel);
/*WIDTH TEXTBOX*/
TextBox widthTextBox = new TextBox();
widthTextBox.Name = "widthTextBox";
widthTextBox.Text = myGrid.Width.ToString();
widthTextBox.Location = new Point(widthLabel.Location.X + offset, widthLabel.Location.Y);
myTabPage.Controls.Add(widthTextBox);
widthTextBox.KeyPress += new KeyPressEventHandler(widthTextBox_KeyPress); //EVENT HANDLER
widthTextBox.KeyUp += new KeyEventHandler(widthTextBox_KeyUp); //EVENT HANDLER
/*HEIGHT LABEL*/
Label heightLabel = new Label();
heightLabel.Text = "Height";
heightLabel.Location = new Point(0, YOffset());
myTabPage.Controls.Add(heightLabel);
/*HEIGHT TEXTBOX*/
TextBox heightTextBox = new TextBox();
heightTextBox.Name = "heightTextBox";
heightTextBox.Text = myGrid.Height.ToString();
heightTextBox.Location = new Point(heightLabel.Location.X + offset, heightLabel.Location.Y);
myTabPage.Controls.Add(heightTextBox);
/*RADIOBUTTON LABEL*/
GroupBox RadioLabel = new GroupBox();
RadioLabel.Text = "Tracking Style";
RadioLabel.Location = new Point(0, YOffset());
myTabPage.Controls.Add(RadioLabel);
/*RADIO BUTTONS*/
RadioButton SelectMouse = new RadioButton();
SelectMouse.Location = new Point(10, 20);
SelectMouse.Text = "Mouse";
SelectMouse.Name = "SelectMouseRadioButton";
SelectMouse.CheckedChanged += new EventHandler(RadioButtons_CheckedChanged);
RadioLabel.Controls.Add(SelectMouse);
RadioButton SelectKeyboard = new RadioButton();
SelectKeyboard.Location = new Point(10, 42);
SelectKeyboard.Text = "Keyboard";
SelectKeyboard.Name = "SelectKeyboardRadioButton";
SelectKeyboard.CheckedChanged += new EventHandler(RadioButtons_CheckedChanged);
RadioLabel.Controls.Add(SelectKeyboard);
heightTextBox.KeyPress += new KeyPressEventHandler(heightTextBox_KeyPress); //EVENT HANDLER
heightTextBox.KeyUp += new KeyEventHandler(heightTextBox_KeyUp); //EVENT HANDLER
//EVENT HANDLER
LeftControlTab.Controls.Add(myTabPage);
Controls.Add(LeftControlTab);
}
private void InitializeGrid()
{
myGrid = new Grid(offsetX, offsetY);
}
private void connectToolStripMenuItem_Click(object sender, EventArgs e)
{
serialConnectionDialogBox serialBox = new serialConnectionDialogBox();
Point temp = this.Location;
temp.X += 30;
temp.Y += 70;
serialBox.Location = temp;
DialogResult results = serialBox.ShowDialog();
if (results == DialogResult.Yes && !myConnection.connected)
{
myConnection.setCOMMPort(serialBox.commPortComboBox.Text);
myConnection.setBaudRate(Convert.ToInt32(serialBox.baudRateComboBox.Text));
myConnection.connect();
}
if (myConnection.connected)
{
ConnectionStatus.Text = "X " + currentX.ToString() + "Y " + currentY.ToString();
}
}
private void InitializeSerial()
{
myConnection = new serialConnection();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
bool updatePos = false;
switch (keyData)
{
case Keys.Left:
currentX += offsetX;
updatePos = true;
break;
case Keys.Right:
currentX -= offsetX;
updatePos = true;
break;
case Keys.Up:
currentY += offsetY;
updatePos = true;
break;
case Keys.Down:
currentY -= offsetY;
updatePos = true;
break;
case Keys.F5:
if (trackingActive)
{
trackingActive = false;
LeftControlTab.Enabled = true;
if(CurrentState == States.KeyboardTracking)
activeStatus.Text = "Keyboard Tracking DEACTIVATED";
if (CurrentState == States.MouseTracking)
activeStatus.Text = "Mouse Tracking DEACTIVATED";
activeStatus.BackColor = Color.Red;
myTimer.Enabled = false;
}
else
{
trackingActive = true;
LeftControlTab.Enabled = false;
if (CurrentState == States.KeyboardTracking)
activeStatus.Text = "Keyboard Tracking ACTIVATED";
if (CurrentState == States.MouseTracking)
activeStatus.Text = "Mouse Tracking ACTIVATED";
activeStatus.BackColor = Color.Green;
myTimer.Enabled = true;
}
break;
}
if (updatePos == true)
{
updatePos = false;
Point temp = new Point();
temp.X = currentX = clipX(currentX);
temp.Y = currentY = clipY(currentY);
String tx = "x," + Convert.ToString(temp.X) + ",y," + Convert.ToString(temp.Y) + ",";
myConnection.sendData(tx);
ConnectionStatus.Text = "X " + currentX.ToString() + "Y " + currentY.ToString();
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void disconnectToolStripMenuItem_Click_1(object sender, EventArgs e)
{
if (myConnection.connected)
{
myConnection.disconnect();
ConnectionStatus.Text = "Disconnected!";
}
}
private void RadioButtons_CheckedChanged(object sender, EventArgs e)
{
if (sender == (RadioButton)this.Controls.Find("SelectMouseRadioButton", true)[0])
{
CurrentState = States.MouseTracking;
activeStatus.Text = "Mouse Tracking ";
}
if (sender == (RadioButton)this.Controls.Find("SelectKeyboardRadioButton", true)[0])
{
CurrentState = States.KeyboardTracking;
activeStatus.Text = "Keyboard Tracking ";
}
if (trackingActive)
activeStatus.Text += "ACTIVATED";
else
activeStatus.Text += "DEACTIVATED";
}
private void TrackMouse(object source, ElapsedEventArgs e)
{
if (trackingActive && CurrentState == States.MouseTracking)
{
MouseMovedFlag = true;
MouseMovedX = -1 * (Cursor.Position.X - XCenter);
MouseMovedY = -1 * (Cursor.Position.Y - YCenter);
currentX += MouseMovedX;
currentX = clipX(currentX);
currentY += MouseMovedY;
currentY = clipY(currentY);
statusStrip1.Invoke(new UpdateStatusBarDelegate(this.UpdateStatusBar), null);
Cursor.Position = new Point(XCenter, YCenter);
}
}
private int clipX(int tempX)
{
if(tempX < minX)
tempX = minX;
if(tempX > maxX)
tempX = maxX;
return tempX;
}
private int clipY(int tempY)
{
if(tempY < minY)
tempY = minY;
if (tempY > maxY)
tempY = maxY;
return tempY;
}
private int YOffset()
{
int tempValue = YOffsetValue;
if (tempValue == 0)
{
YOffsetValue += 22;
return tempValue;
}
else
{
YOffsetValue += 22;
return tempValue;
}
}
void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (true)
{
if (MouseMovedFlag || trackingActive)
{
Point temp = new Point();
temp.X = currentX;
temp.Y = currentY;
String tx = "x," + Convert.ToString(temp.X) + ",y," + Convert.ToString(temp.Y) + ",";
myConnection.sendData(tx);
}
}
}
void UpdateStatusBar()
{
ConnectionStatus.Text = "X " + currentX.ToString() + "Y " + currentY.ToString();
ConnectionStatus.Invalidate();
this.Update();
}
}
}
Related
I'm looking for the method for moving and changing the size of shape using mouse #C.
That shape made by mouse location (Point start, end) on pictureBox named "captureDesign".
I wanted to ask for a little bit of help.
I searched many similar case of question, but I couldn't solve it yet.
IF possible, please let me know to how.
Here is my code.
It's not everything, for example, I omitted the contents about the mode selection for shape using Button_click.
I studied the similar case.
But I haven't noticed it yet.
How can I associate startPt (#MouseDown) and endPt (#MouseUp) with MyMove to make the move successful?
MyMove code is written in upper link. I need it change.
Actually I need to code for change the size but, first of all, I want to move that using mouse.
namespace Pilot
{
enum DrawMode { LINE, RECTANGLE, CIRCLE, NUMBER };
public partial class mainForm : Form
{
#region define
private bool _isCaptionShow = false;
private ScreenPicture sp;
private IContainer components = null;
Bitmap bitmap;
private DrawMode drawMode;
private Graphics g;
private Pen pen = new Pen(Color.Red, 7);
Point startPt, endPt, currPt, prevPt, addPt;
private int numberCount = 0;
int rectWidth, rectHeight;
Font font = new Font("Arial", 12);
private bool selectMode = false;
private void selectModeButton_CheckedChanged(object sender, EventArgs e)
{
if (selectModeButton.Checked == true)
selectMode = true;
else
selectMode = false;
}
MyMove m;
Point deltaStart;
Point deltaEnd;
bool dragging = false;
#region Contents on PictureBox "captureDesign;" when mouse clicked.
private void captureDesign_MouseDown(object sender, MouseEventArgs e)
{
startPt = new Point(e.X, e.Y);
prevPt = startPt;
currPt = startPt;
if (selectMode)
{
if (e.Button == MouseButtons.Left && m.IsPointOnLine(e.Location, 5))
{
dragging = true;
deltaStart = new Point(startPt.X- e.Location.X, startPt.Y - e.Location.Y);
}
}
}
#region Contents on PictureBox captureDesign when Mouse dropped.
private void captureDesign_MouseUp(object sender, MouseEventArgs e)
{
g = captureDesign.CreateGraphics();
endPt = new Point(e.X, e.Y);
m = new MyMove(pen, startPt, endPt);
#region calculate between start Point ~ end Point to width, height
if (endPt.X < startPt.X)
{
rectWidth = Math.Abs(endPt.X - startPt.X);
addPt.X = endPt.X;
}
else
{
rectWidth = Math.Abs(endPt.X - startPt.X);
addPt.X = startPt.X;
}
if (endPt.Y < startPt.Y)
{
rectHeight = Math.Abs(endPt.Y - startPt.Y);
addPt.Y = endPt.Y;
}
else
{
rectHeight = Math.Abs(endPt.Y - startPt.Y);
addPt.Y = startPt.Y;
}
#endregion
if (selectMode)
{
deltaEnd = new Point(endPt.X - e.Location.X, endPt.Y - e.Location.Y);
}
else //No selectMode
{
#region draw the shape in case of drawMode
switch (drawMode)
{
case DrawMode.LINE:
if (arrowCheck.Checked == true)
{
pen.StartCap = LineCap.ArrowAnchor;
}
else
//g.DrawLine(pen, startPt, endPt);
g.DrawLine(m.mpen, m.mStart, m.mEnd);
break;
case DrawMode.RECTANGLE:
//g.DrawRectangle(pen, new Rectangle(startPt, new Size(endPt.X - startPt.X, endPt.Y - startPt.Y)));
g.DrawRectangle(pen, new Rectangle(addPt, new Size(rectWidth, rectHeight)));
break;
case DrawMode.CIRCLE:
g.DrawEllipse(pen, new Rectangle(addPt, new Size(rectWidth, rectHeight)));
break;
case DrawMode.NUMBER:
numberCount++;
g.DrawString(numberCount.ToString(), font, Brushes.White, endPt);
break;
}
#endregion
}
}
#region
private void captureDesign_MouseMove(object sender, MouseEventArgs e)
{
if (dragging && deltaStart != null && deltaEnd != null)
{
m.mStart = new Point(deltaStart.X + e.Location.X, deltaStart.Y + e.Location.Y);
m.mEnd = new Point(deltaEnd.X + e.Location.X, deltaEnd.Y + e.Location.Y);
}
}
}
public class MyMove
{
public Pen mpen { get; set; }
public Point mStart { get; set; }
public Point mEnd { get; set; }
public MyMove(Pen p, Point p1, Point p2)
{
mpen = p;
mStart = p1;
mEnd = p2;
}
public float slope
{
get
{
return (((float)mEnd.Y - (float)mStart.Y) / ((float)mEnd.X - (float)mStart.X));
}
}
public float YIntercept
{
get
{
return mStart.Y - slope * mStart.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;
}
}
}
1ST ANSWER
Everything happens on MouseMove(object sender, MouseEventArgs e).
Here is a sample
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
//If we are not allowed to draw, simply return and disregard the rest of the code
if (!_canDraw) return;
//The x-value of our rectangle should be the minimum between the start x-value and the current x-position
int x = Math.Min(_startX, e.X);
//The y-value of our rectangle should also be the minimum between the start y-value and current y-value
int y = Math.Min(_startY, e.Y);
//The width of our rectangle should be the maximum between the start x-position and current x-position minus
//the minimum of start x-position and current x-position
int width = Math.Max(_startX, e.X) - Math.Min(_startX, e.X);
//For the hight value, it's basically the same thing as above, but now with the y-values:
int height = Math.Max(_startY, e.Y) - Math.Min(_startY, e.Y);
_rect = new Rectangle(x, y, width, height);
//Refresh the form and draw the rectangle
Refresh();
}
protected override void OnPaint(PaintEventArgs e)
{
//Create a new 'pen' to draw our rectangle with, give it the color red and a width of 2
using (Pen pen = new Pen(Color.Red, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, _rect);
}
}
You can further study the detail here, C# Tutorial - Drawing rectangles with the mouse.
2ND ANSWER (UPDATE)
I achieve the solution you want but I use a third party library MoveGraphLibrary. You can further read this article Moveable Resizable Objects.
Sample Code
GRAPHICAL OBJECT
public class Rectangle : GraphicalObject
{
protected RectangleF rc;
protected Resizing resize;
protected float wMin, wMax, hMin, hMax;
protected int radius;
protected int halfstrip;
protected SolidBrush brush;
int minsize = 25;
// -------------------------------------------------
public Rectangle(RectangleF rect, RectRange range, int rad, int half, Color color)
{
rc = new RectangleF(rect.X, rect.Y, Math.Max(minsize, rect.Width), Math.Max(minsize, rect.Height));
if (range == null)
{
wMin = wMax = rc.Width;
hMin = hMax = rc.Height;
}
else
{
wMin = Math.Max(minsize, Math.Min(rc.Width, range.MinWidth));
wMax = Math.Max(rc.Width, range.MaxWidth);
hMin = Math.Max(minsize, Math.Min(rc.Height, range.MinHeight));
hMax = Math.Max(rc.Height, range.MaxHeight);
}
RectRange realrange = new RectRange(wMin, wMax, hMin, hMax);
resize = realrange.Resizing;
radius = rad;
halfstrip = half;
brush = new SolidBrush(color);
}
// -------------------------------------------------
// ------------------------------------------------- RectAround
new public RectangleF RectAround
{
get { return (rc); }
}
// ------------------------------------------------- Radius
public int Radius
{
get { return (radius); }
set
{
radius = Math.Abs(value);
DefineCover();
}
}
// ------------------------------------------------- HalfStrip
public int HalfStrip
{
get { return (halfstrip); }
set
{
halfstrip = Math.Abs(value);
DefineCover();
}
}
// ------------------------------------------------- Color
public Color Color
{
get { return (brush.Color); }
set { brush.Color = value; }
}
// -------------------------------------------------
public void Draw(Graphics grfx)
{
grfx.FillRectangle(brush, rc);
}
// ------------------------------------------------- Resizing
public Resizing Resizing
{
get { return (resize); }
set
{
resize = value;
DefineCover();
}
}
// ------------------------------------------------- DefineCover
public override void DefineCover()
{
cover = new Cover(rc, resize, radius, halfstrip);
}
// -------------------------------------------------
public override void Move(int dx, int dy)
{
rc.X += dx;
rc.Y += dy;
}
// ------------------------------------------------- MoveNode
public override bool MoveNode(int iNode, int dx, int dy, Point ptM, MouseButtons catcher)
{
bool bRet = false;
if (catcher == MouseButtons.Left)
{
float wNew, hNew;
switch (resize)
{
case Resizing.Any:
if (iNode == 8)
{
Move(dx, dy);
}
else if (iNode == 0) //LT corner
{
hNew = rc.Height - dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Top(dy);
bRet = true;
}
wNew = rc.Width - dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Left(dx);
bRet = true;
}
}
else if (iNode == 1) // RT corner
{
hNew = rc.Height - dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Top(dy);
bRet = true;
}
wNew = rc.Width + dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Right(dx);
bRet = true;
}
}
else if (iNode == 2) // RB corner
{
wNew = rc.Width + dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Right(dx);
bRet = true;
}
hNew = rc.Height + dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Bottom(dy);
bRet = true;
}
}
else if (iNode == 3) // LB corner
{
hNew = rc.Height + dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Bottom(dy);
bRet = true;
}
wNew = rc.Width - dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Left(dx);
bRet = true;
}
}
else if (iNode == 4) // on left side
{
wNew = rc.Width - dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Left(dx);
bRet = true;
}
}
else if (iNode == 5) // on right side
{
wNew = rc.Width + dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Right(dx);
bRet = true;
}
}
else if (iNode == 6) // on top
{
hNew = rc.Height - dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Top(dy);
bRet = true;
}
}
else if (iNode == 7) // on bottom
{
hNew = rc.Height + dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Bottom(dy);
bRet = true;
}
}
break;
case Resizing.NS:
if (iNode == 2)
{
Move(dx, dy);
}
else if (iNode == 0) // on top
{
hNew = rc.Height - dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Top(dy);
bRet = true;
}
}
else if (iNode == 1) // on bottom
{
hNew = rc.Height + dy;
if (hMin <= hNew && hNew <= hMax)
{
MoveBorder_Bottom(dy);
bRet = true;
}
}
break;
case Resizing.WE:
if (iNode == 2)
{
Move(dx, dy);
}
else if (iNode == 0) // on left side
{
wNew = rc.Width - dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Left(dx);
bRet = true;
}
}
else if (iNode == 1) // on right side
{
wNew = rc.Width + dx;
if (wMin <= wNew && wNew <= wMax)
{
MoveBorder_Right(dx);
bRet = true;
}
}
break;
case Resizing.None:
Move(dx, dy);
break;
}
}
return (bRet);
}
// ------------------------------------------------- MoveBorder_Top
private void MoveBorder_Top(int dy)
{
rc.Y += dy;
rc.Height -= dy;
}
// ------------------------------------------------- MoveBorder_Bottom
private void MoveBorder_Bottom(int dy)
{
rc.Height += dy;
}
// ------------------------------------------------- MoveBorder_Left
private void MoveBorder_Left(int dx)
{
rc.X += dx;
rc.Width -= dx;
}
// ------------------------------------------------- MoveBorder_Right
private void MoveBorder_Right(int dx)
{
rc.Width += dx;
}
public PointF Location
{
get
{
return rc.Location;
}
}
}
MAIN FORM
public partial class Form1 : Form
{
RectRange rr;
// Variables use for Moving & Resizing
NumericUpDown numericUD_Radius = new NumericUpDown();
NumericUpDown numericUD_HalfStrip = new NumericUpDown();
string[] strs = new string[] {"Circles' radius",
"Half strip width"
};
Mover mover;
Point ptMouse_Down;
bool bShowCovers = false;
RigidlyBoundRectangles rigidrectsView;
List<Shapes.Rectangle> rects = new List<Shapes.Rectangle>();
int radius, halfstrip;
// Variables use for Drawing
bool isMouseDown = false;
public Form1()
{
InitializeComponent();
lblXAxis.Text = $"X Axis: -";
lblYAxis.Text = $"Y Axis: -";
numericUD_Radius.Value = 6;
numericUD_HalfStrip.Value = 3;
mover = new Mover(panel1);
SizeF[] sizefStrs = Auxi_Geometry.MeasureStrings(this, strs);
rigidrectsView = new RigidlyBoundRectangles(new Control[] { numericUD_Radius, numericUD_HalfStrip });
rigidrectsView.Add(Auxi_Geometry.RectangleToRectangleSide(numericUD_Radius.Bounds, Side.E, sizefStrs[0], 4), "Radius");
rigidrectsView.Add(Auxi_Geometry.RectangleToRectangleSide(numericUD_HalfStrip.Bounds, Side.E, sizefStrs[1], 4), "Strip");
rigidrectsView.AddUnionRectangle();
radius = Convert.ToInt32(numericUD_Radius.Value);
halfstrip = Convert.ToInt32(numericUD_HalfStrip.Value);
rr = new RectRange(panel1.MinimumSize.Width, panel1.Size.Width, panel1.MinimumSize.Height, panel1.Size.Height);
rects.Add(new Shapes.Rectangle(new RectangleF(100, 100, 300, 400), rr, radius, halfstrip, Color.Black));
RenewMover();
}
private void RenewMover()
{
mover.Clear();
mover.Insert(0, rigidrectsView);
for (int i = 0; i < rects.Count; i++)
{
mover.Add(rects[i]);
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics grfx = e.Graphics;
GraphicalObject grobj;
for (int i = mover.Count - 1; i >= 0; i--)
{
grobj = mover[i].Source;
if (grobj is Shapes.Rectangle)
{
(grobj as Shapes.Rectangle).Draw(grfx);
}
if (bShowCovers)
{
mover[i].DrawCover(grfx);
}
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
lblXAxis.Text = $"X Axis: {e.X}";
lblYAxis.Text = $"Y Axis: {e.Y}";
if (rbMoveOrResize.Checked && mover.Move(e.Location))
{
panel1.Invalidate();
}
else
{
if (isMouseDown)
{
var rectangle = rects.Last();
var drawRectangle = new Shapes.Rectangle(new RectangleF(rectangle.Location.X,
rectangle.Location.Y,
e.X - rectangle.Location.X,
e.Y - rectangle.Location.Y),
rr, radius, halfstrip, Color.Black);
rects.Remove(rects.Last());
rects.Add(drawRectangle);
RenewMover();
panel1.Invalidate();
}
}
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
lblXAxis.Text = $"X Axis: -";
lblYAxis.Text = $"Y Axis: -";
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
if (rbMoveOrResize.Checked)
{
ptMouse_Down = e.Location;
mover.Catch(e.Location, e.Button);
}
else
{
isMouseDown = true;
rects.Add(new Shapes.Rectangle(new RectangleF(e.Location.X, e.Location.Y, 0, 0), rr, radius, halfstrip, Color.Black));
}
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
if (rbMoveOrResize.Checked && mover.Release())
{
if (e.Button == MouseButtons.Left &&
Auxi_Geometry.Distance(ptMouse_Down, e.Location) <= 3)
{
GraphicalObject grobj = mover[mover.ReleasedObject].Source;
if (grobj is Shapes.Rectangle)
{
PopupRectangle(grobj.ID);
}
}
}
else
{
isMouseDown = false;
}
}
private void rb_CheckedChanged(object sender, EventArgs e)
{
bShowCovers = rbMoveOrResize.Checked;
panel1.Invalidate();
}
private void PopupRectangle(long id)
{
for (int i = rects.Count - 1; i > 0; i--)
{
if (id == rects[i].ID)
{
Shapes.Rectangle elem = rects[i];
rects.RemoveAt(i);
rects.Insert(0, elem);
RenewMover();
panel1.Invalidate();
break;
}
}
}
}
SAMPLE OUTPUT
I am currently working on a topdown game made in windows forms. In the game, your character rotates after your mouse and you can shoot a shot from your character in the direction of your mouse. To create multiple areas in the game, I chose to use groupboxes as separate areas in the game that you can switch between using buttons but I've run in to a problem. I can no longer rotate or move my character or even shoot within the groupboxes.
I've tried setting breakpoints and discovered that the keyup, keydown and mousemove methods are not being called but I don't know why. For some reason I get an error when i release space in the btnSave_Click method which i marked in the code.
static Image originalImage;
bool pRight, pLeft, pUp, pDown;
string[] Saves;
Save[] RSaves = new Save[4];
Save yoursave;
Save temp;
int slot;
NewGame newgame;
SavedGames savedgames;
string savedata, name = "";
int lvl = 1;
double exp = 0, money = 0;
int pSpeed = 5;
double deltaY, deltaX;
float interval = 7;
Point start;
Point nextStart;
Point cursor;
float radian;
const double Rad2Grad = (180 / Math.PI);
public MainGame()
{
InitializeComponent();
pbPlayer.BringToFront();
gbxTown.AllowDrop = true;
gbxQ1.AllowDrop = true;
pbShot.Location = pbPlayer.Location;
// newgame = new NewGame();
// savedgames = new SavedGames();
originalImage = pbPlayer.Image;
}
public void setSaves(string savedata, int slot)
{
Saves = savedata.Split('#');
string a1 = Saves[0];
string a2 = Saves[1];
string a3 = Saves[2];
string a4 = Saves[3];
RSaves[0] = temp.StringToSaves(temp, a1);
RSaves[1] = temp.StringToSaves(temp, a2);
RSaves[2] = temp.StringToSaves(temp, a3);
RSaves[3] = temp.StringToSaves(temp, a4);
yoursave = RSaves[slot - 1];
name = yoursave.getName();
this.slot = slot;
Controls.Add(pbPlayer);
Controls.Add(pbShot);
}
private void MainGame_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.A)
{
pRight = false;
pLeft = true;
}
else if (e.KeyData == Keys.D)
{
pRight = true;
pLeft = false;
}
else if (e.KeyData == Keys.S)
{
pUp = true;
pDown = false;
}
else if (e.KeyData == Keys.W)
{
pUp = false;
pDown = true;
}
if (e.KeyData == Keys.Space)
{
start = pbPlayer.Location;
cursor = Cursor.Position;
nextStart = start;
deltaY = cursor.Y - start.Y;
deltaX = cursor.X - start.X;
double test = Angle(start, cursor);
radian = (float)(Angle(start, cursor) - 175);
timer2.Enabled = true;
}
}
private void MainGame_KeyUp_1(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.A)
{
pLeft = false;
}
else if (e.KeyData == Keys.D)
{
pRight = false;
}
else if (e.KeyData == Keys.S)
{
pUp = false;
}
else if (e.KeyData == Keys.W)
{
pDown = false;
}
if (e.KeyData == Keys.Space)
{
timer2.Stop();
pbShot.Location = start;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
MovePlayer();
//checkCollision();
}
private void btnNextArea_Click(object sender, EventArgs e)
{
pbPlayer.Parent = gbxQ1;
pbShot.Parent = gbxQ1;
gbxQ1.Location = new Point(0, 0);
gbxTown.Location = new Point(605, 0);
pbPlayer.Location = new Point(100, 200);
pbShot.Location = pbPlayer.Location;
}
private void btnSave_Click(object sender, EventArgs e)
{
newgame = new NewGame();
savedgames = new SavedGames();
RSaves[slot - 1] = new Save(name, money, lvl, exp);
for (int i = 0; i < 4; i++) //When the Spacebar is released, a System.IndexOutOfRangeException error happens for i.
{
Saves[i] = RSaves[i].SavesToString();
}
savedata = Saves[0] + Saves[1] + Saves[2] + Saves[3];
System.IO.File.WriteAllLines(#"saves.txt", Saves);
newgame.setSaves(savedata);
savedgames.setSaves(savedata);
}
private void btnBack_Click(object sender, EventArgs e)
{
pbPlayer.Parent = gbxTown;
pbShot.Parent = gbxTown;
gbxTown.Location = new Point(0, 0);
gbxQ1.Location = new Point(605, 0);
pbPlayer.Location = new Point(100, 200);
pbShot.Location = pbPlayer.Location;
}
private void MainGame_MouseMove_1(object sender, MouseEventArgs e)
{
var y2 = e.Y;
var y1 = (this.pbPlayer.Location.Y + (this.pbPlayer.Height / 2));
var x2 = e.X;
var x1 = (this.pbPlayer.Location.X + (this.pbPlayer.Width / 2));
var angle = (float)Math.Atan2((y1 - y2), (x1 - x2));
pbPlayer.Image = RotateImage(originalImage, (angle * 57));
}
private double Angle(Point start, Point end)
{
return (float)Math.Atan2(end.Y - start.Y, end.X - start.X) * 57;
}
private void timer2_Tick_1(object sender, EventArgs e)
{
nextStart.X -= Convert.ToInt16(interval * ((float)Math.Cos(radian / Rad2Grad)));
nextStart.Y -= Convert.ToInt16(interval * ((float)Math.Sin(radian / Rad2Grad)));
pbShot.Location = nextStart;
}
public static Image RotateImage(Image img, float rotationAngle)
{
Bitmap bmp = new Bitmap(img.Width, img.Height);
Graphics gfx = Graphics.FromImage(bmp);
gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);
gfx.RotateTransform(rotationAngle);
gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.DrawImage(img, new Point(0, 0));
gfx.Dispose();
return bmp;
}
private void MovePlayer()
{
if (pRight == true && pbPlayer.Left < 900)
{
pbPlayer.Left += pSpeed;
}
else if (pLeft == true && pbPlayer.Left > 0)
{
pbPlayer.Left -= pSpeed;
}
else if (pUp == true && pbPlayer.Top < 600)
{
pbPlayer.Top += pSpeed;
}
else if (pDown == true && pbPlayer.Top > 0)
{
pbPlayer.Top -= pSpeed;
}
}
The expected outcome is for the movement, aiming and firing to work as intended within the groupboxes but currently they do not execute in the code while in the groupboxes. It works fine in the form but not in the groupboxes. I would appriciate any help.
I'm experimenting with a form that contains some vertically stacked panels and I'm trying to implement some drag and drop so they can be re-ordered. It's working well so far apart from one bug that I can't figure out.
The premise is that it's a list of files which must be kept vertically stacked, but can be re-ordered as the user sees fit, to form a list of pages for a document I'll produce later.
Try the following code and you should get a form with 6 stacked panels. Click and drag any one of them up or down and they will re-order.
The problem is that if you drag one up and down really fast they start to overlap with each other.
I've put the shortcut CTRL + B in there to list the current panel top positions at any given time and you'll see that you get duplicate numbers when you shouldn't.
The problem is with the MouseMove event. I'm sure it's something like a race condition where the dictionary can't keep up or the indexes don't calculate fast enough but I'm scratching my head. I've tried locking them but it hasn't helped.
I'm sure there's a better way to implement this so I'm open to suggestions, but given that this will never have more than say 30 panels this suits my needs.
Apologies for the messy-ness, I was planning to tidy up later!
public Form1()
{
InitializeComponent();
this.SuspendLayout();
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 601);
this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(300, 640);
this.MinimumSize = new System.Drawing.Size(300, 640);
this.Name = "Form1";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
this.ResumeLayout(false);
}
Dictionary<int, Panel> panelPositions = new Dictionary<int, Panel>();
static Random rnd = new Random();
int y;
int start;
static int index, previndex, currentindex, newindex, maxindex;
bool isDragging;
Panel currentPanel;
static readonly object Lock = new Object();
private void Form1_Load(object sender, EventArgs e)
{
Width = 300;
Height = 640;
int count = 5;
int currentTop = 0;
for (int i = 0; i <= count; i++)
{
Panel panel = new Panel();
panel.Width = 300;
panel.Height = 100;
panel.Top = currentTop;
panel.BackColor = RandomColor();
panel.Margin = Padding.Empty;
panel.Padding = Padding.Empty;
panel.BorderStyle = BorderStyle.None;
Label label = new Label();
label.Font = new Font("Segoe UI", 24.0f);
label.Text = (i + 1).ToString();
label.Top = 20;
label.Left = 20;
label.AutoSize = true;
panel.Controls.Add(label);
panel.MouseDown += new MouseEventHandler(MouseisDown);
panel.MouseMove += new MouseEventHandler(MouseMoves);
panel.MouseUp += new MouseEventHandler(MouseisUp);
lock (Lock)
{
panelPositions.Add(i, panel);
}
Controls.Add(panel);
currentTop += 100;
}
lock (Lock)
{
maxindex = panelPositions.Count - 1;
}
}
private void MouseisUp(object sender, MouseEventArgs e)
{
if (isDragging)
{
if (newindex < maxindex)
{
currentPanel.Top = newindex * 100;
}
else
{
currentPanel.Top = maxindex * 100;
}
}
isDragging = false;
}
// I'M SURE THE PROBLEM IS IN HERE SOMEWHERE.
private void MouseMoves(object sender, MouseEventArgs e)
{
// CHECK THE MOUSE IS STILL DOWN
if (isDragging)
{
// DRAG PANEL VERTICALLY WITH MOUSE
currentPanel.Location = new Point(currentPanel.Left, e.Y + currentPanel.Top - y);
// WORK OUT NEW INDEX POSITION
newindex = 0;
if ((currentPanel.Top + e.Y) > 0)
{
newindex = ((currentPanel.Top + e.Y) / 100);
}
// NEW POSITION?
if (currentindex != newindex)
{
// TRACK CHANGES
previndex = currentindex;
currentindex = newindex;
/* PRETTY SURE IT'S THIS BIT THAT'S WRONG */
// CHECK WE'RE NOT OUT OF BOUNDS
if (currentindex <= maxindex)
{
lock (Lock)
{
// RE-ARRANGE PANEL INDEX
panelPositions[previndex] = panelPositions[currentindex];
panelPositions[currentindex] = currentPanel;
panelPositions[previndex].Top = previndex * 100;
}
}
}
}
}
private void MouseisDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
currentPanel = sender as Panel;
isDragging = true;
currentPanel.BringToFront();
y = e.Y;
start = currentPanel.Top;
int i = 0;
if (currentPanel.Top > 0)
{
i = currentPanel.Top;
i = (i / 100);
}
index = i;
previndex = i;
currentindex = i;
}
else
{
isDragging = false;
}
}
private Color RandomColor()
{
Color randColor;
randColor = Color.FromArgb(RandomRGB(), RandomRGB(), RandomRGB());
return randColor;
}
private int RandomRGB()
{
return rnd.Next(1, 256);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.B)
{
string message = string.Empty;
int count = panelPositions.Count;
for (int i = 0; i < count; i++)
{
message += #"Panel " + i.ToString() + #": " + panelPositions[i].Top.ToString() + Environment.NewLine;
}
MessageBox.Show(message);
}
}
Thanks!
Edit: I think it's something to do with when the panel goes outside the bottom bounds of the form. If you drag within the form the issue doesn't occur. I think it's to do with the previous index, but I can't see it.
Change
// NEW POSITION?
if (currentindex != newindex)
To
// NEW POSITION?
if (currentindex != newindex && newindex <= maxindex)
I think you know why :)
P.s: Your "Edit" section is a bit misleading.
I tried to compile this code but it won't work, getting this error upon compilation:
Pong\Form1.Designer.cs(14,33,14,40): error CS0115: 'Pong.Form1.Dispose(bool)': no suitable method found to override
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Media;
namespace Pong
{
public partial class gameArea : Form
{
PictureBox picBoxPlayer, picBoxAI, picBoxBall;
Timer gameTime; // also the game loop
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
Size sizePlayer = new Size(25, 100);
Size sizeAI = new Size(25, 100);
Size sizeBall = new Size(20, 20);
const int gameTimeInterval = 1;
const int ballStartSpeed = 2;
const int ballIncreaseSpeedRate = 1;
const int ballSpeedLimited = 15;
const int aiOffSetLoops = 15;
int ballSpeedX = ballStartSpeed;
int ballSpeedY = ballStartSpeed;
Random rad;
int aiOffSet;
int aiOffSetCounter;
Dictionary<string, SoundPlayer> sounds;
public gameArea()
{
InitializeComponent();
this.DoubleBuffered = true;
picBoxPlayer = new PictureBox();
picBoxAI = new PictureBox();
picBoxBall = new PictureBox();
gameTime = new Timer();
gameTime.Interval = gameTimeInterval;
gameTime.Tick += new EventHandler(gameTime_Tick);
this.Width = SCREEN_WIDTH;
this.Height = SCREEN_HEIGHT;
this.StartPosition = FormStartPosition.CenterScreen;
this.BackColor = Color.Black;
picBoxPlayer.Size = sizePlayer;
picBoxPlayer.Location = new Point(picBoxPlayer.Width / 2, ClientSize.Height / 2 - picBoxPlayer.Height / 2);
picBoxPlayer.BackColor = Color.Blue;
this.Controls.Add(picBoxPlayer);
picBoxAI.Size = sizeAI;
picBoxAI.Location = new Point(ClientSize.Width - (picBoxAI.Width + picBoxAI.Width / 2), ClientSize.Height / 2 - picBoxPlayer.Height / 2); // TODO: why picBoxPlayer and not picBoxAI?
picBoxAI.BackColor = Color.Red;
this.Controls.Add(picBoxAI);
rad = new Random();
aiOffSet = 0;
aiOffSetCounter = 1;
picBoxBall.Size = sizeBall;
picBoxBall.Location = new Point(ClientSize.Width / 2 - picBoxBall.Width / 2, ClientSize.Height / 2 - picBoxBall.Height / 2);
picBoxBall.BackColor = Color.Green;
this.Controls.Add(picBoxBall);
// Load Sounds
sounds = new Dictionary<string, SoundPlayer>();
for (int k = 1; k <= 10; k++)
{
sounds.Add(String.Format(#"pong{0}", k), new SoundPlayer(String.Format(#"pong{0}.wav", k)));
}
// Start Game loop
gameTime.Enabled = true;
}
void gameTime_Tick(object sender, EventArgs e)
{
picBoxBall.Location = new Point(picBoxBall.Location.X + ballSpeedX, picBoxBall.Location.Y + ballSpeedY);
gameAreaCollosions();
padlleCollision();
playerMovement();
aiMovement();
}
private void iaChangeOffSet()
{
if (aiOffSetCounter >= aiOffSetLoops)
{
aiOffSet = rad.Next(1, picBoxAI.Height + picBoxBall.Height);
aiOffSetCounter = 1;
}
else
{
aiOffSetCounter++;
}
}
private void gameAreaCollosions()
{
if (picBoxBall.Location.Y > ClientSize.Height - picBoxBall.Height || picBoxBall.Location.Y < 0)
{
iaChangeOffSet();
ballSpeedY = -ballSpeedY;
sideCollision();
}
else if (picBoxBall.Location.X > ClientSize.Width)
{
padlleSideCollision();
resetBall();
}
else if (picBoxBall.Location.X < 0)
{
padlleSideCollision();
resetBall();
}
}
private void resetBall()
{
if (ballSpeedX > 0)
ballSpeedX = -ballStartSpeed;
else
ballSpeedX = ballStartSpeed;
if (ballSpeedY > 0)
ballSpeedY = -ballStartSpeed;
else
ballSpeedY = ballStartSpeed;
aiOffSet = 0;
picBoxBall.Location = new Point(ClientSize.Width / 2 - picBoxBall.Width / 2, ClientSize.Height / 2 - picBoxBall.Height / 2);
}
private void playerMovement()
{
if (this.PointToClient(MousePosition).Y >= picBoxPlayer.Height / 2 && this.PointToClient(MousePosition).Y <= ClientSize.Height - picBoxPlayer.Height / 2)
{
int playerX = picBoxPlayer.Width / 2;
int playerY = this.PointToClient(MousePosition).Y - picBoxPlayer.Height / 2;
picBoxPlayer.Location = new Point(playerX, playerY);
}
}
private void aiMovement()
{
int aiX = ClientSize.Width - (picBoxAI.Width + picBoxAI.Width / 2);
int aiY = (picBoxBall.Location.Y - picBoxAI.Height / 2) + aiOffSet;
if (aiY < 0)
aiY = 0;
if (aiY > ClientSize.Height - picBoxAI.Height)
aiY = ClientSize.Height - picBoxAI.Height;
picBoxAI.Location = new Point(aiX, aiY);
}
private void padlleCollision()
{
if (picBoxBall.Bounds.IntersectsWith(picBoxAI.Bounds))
{
picBoxBall.Location = new Point(picBoxAI.Location.X - picBoxBall.Width, picBoxBall.Location.Y);
ballSpeedX = -ballSpeedX;
aiCollision();
}
if (picBoxBall.Bounds.IntersectsWith(picBoxPlayer.Bounds))
{
picBoxBall.Location = new Point(picBoxPlayer.Location.X + picBoxPlayer.Width, picBoxBall.Location.Y);
ballSpeedX = -ballSpeedX;
playerCollision();
}
}
private void playerCollision()
{
sounds["pong1"].Play();
SlowDownBall();
}
private void aiCollision()
{
sounds["pong2"].Play();
SlowDownBall();
}
private void sideCollision()
{
sounds["pong3"].Play();
SpeedUpBall();
}
private void padlleSideCollision()
{
sounds["pong9"].Play();
}
private void SpeedUpBall()
{
if (ballSpeedY > 0)
{
ballSpeedY += ballIncreaseSpeedRate;
if (ballSpeedY >= ballSpeedLimited)
ballSpeedY = ballSpeedLimited;
}
else
{
ballSpeedY -= ballIncreaseSpeedRate;
if (ballSpeedY <= -ballSpeedLimited)
ballSpeedY = -ballSpeedLimited;
}
if (ballSpeedX > 0)
{
ballSpeedX += ballIncreaseSpeedRate;
if (ballSpeedX >= ballSpeedLimited)
ballSpeedX = ballSpeedLimited;
}
else
{
ballSpeedX -= ballIncreaseSpeedRate;
if (ballSpeedX <= -ballSpeedLimited)
ballSpeedX = -ballSpeedLimited;
}
}
private void SlowDownBall()
{
if (ballSpeedY > 0)
{
ballSpeedY -= ballIncreaseSpeedRate;
if (ballSpeedY <= ballStartSpeed)
ballSpeedY = ballStartSpeed;
}
else
{
ballSpeedY += ballIncreaseSpeedRate;
if (ballSpeedY >= -ballStartSpeed)
ballSpeedY = -ballStartSpeed;
}
if (ballSpeedX > 0)
{
ballSpeedX -= ballIncreaseSpeedRate;
if (ballSpeedX <= ballStartSpeed)
ballSpeedX = ballStartSpeed;
}
else
{
ballSpeedX += ballIncreaseSpeedRate;
if (ballSpeedX >= -ballStartSpeed)
ballSpeedX = -ballStartSpeed;
}
}
}
}
As hinted in the comments, changing the class name in the .cs file without updating other references can break things. You started with a Form1 class, changed one of the references to gameArea, but not the other references. As a result, you have two classes, gameArea which derives from Form, and Form1 which implicitly derives from object. Form1 contains the Dispose method, but object doesn't have any Dispose method to override.
To fix this, first change gameArea back to Form1. That should let your code compile again. Then open the designer, and use that to rename Form1 to gameArea. This will update a bit more than you manually did.
Hans Passant points out in the comments that it's also possible to use "Rename" option from the context menu (under "Refactor") instead of the designer. This will update any references in the code without going through the process of creating a design-time form and saving that. As a result, may have slightly different results from what renaming in the designer would get you, for example if your code accesses your form's Name property, but for most programs, it should be good enough.
So i'm in a bit of a problem, i'm coding a clap sensor, that hears when someone claps and executes a certain command.
//CLAP
private float bigValue;
WaveIn waveIn;
private double MaxValue;
private void button1_Loaded(object sender, RoutedEventArgs e)
{
if (Convert.ToInt16(textBox1.Text) > 100)
{
MessageBox.Show("Invalid Value");
return;
}
else
MaxValue = Convert.ToDouble(textBox1.Text) / 100;
bigValue = 0;
waveIn = new WaveIn();
int waveInDevices = waveIn.DeviceNumber;
//Get Device Count
for ( int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
{
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
}
waveIn.DeviceNumber = 0;
waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
int sampleRate = 8000;
int channels = 1;
waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
waveIn.StartRecording();
}
//CLAP
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
for (int index = 0; index < e.BytesRecorded; index += 2)
{
short sample = (short)((e.Buffer[index + 1] << 8) | e.Buffer[index + 0]);
float sample32 = sample / 32768f;
label1.Content = sample32.ToString();
if (bigValue < sample32)
{
bigValue = sample32;
label2.Content = bigValue.ToString();
if (bigValue > MaxValue)
{
waveIn.StopRecording();
SendMessage(MONITOR_ON, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON);
MessageBox.Show("Did you Clap?");
}
}
}
}
The code itself works as is, but I need it to be able to reset itself as many times as I need. This program basically listens for a clap and wakes up the monitor and starts it up. The program breaks any time I add in another "waveIn.StartRecording();"
Any ideas on how I could refresh the page or make it listen for ever?
What basically your code is doing is opening waveIn to receive audio data, then examining the data for loud samples. When it receives a sample that exceeds a threshold it then stops listening and issues the command.
As written, the code stops after the first large sample is detected. No more audio data is received, etc. Probably not what you want. Instead you need to refine your clap detection so that it will stop processing the incoming data for a period of time - a few seconds say - after it detects the first big sample. Don't stop receiving the audio data, just stop reacting to it.
Add a DataTime field to your class that records the timestamp of the last clap detection. At the start of your waveIn_DataAvailable method check if the elapsed time since the last detection is less than your silence time, and if so just return without processing the audio block. When you detect a large enough sample, fire off the event and update the last clap detection field.
Something like this:
DateTime LastDetection = DateTime.Now.AddMinutes(-1);
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
if (LastDetection.AddSeconds(3) >= DateTime.Now)
return;
if (DetectClap(e.Buffer))
{
LastDetection = DateTime.Now;
SendMessage(MONITOR_ON, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON);
MessageBox.Show("Clap detected.");
}
}
bool DetectClap(byte[] audiobytes)
{
for (int i = 0; i < audiobytes.Length; i += 2)
{
float sample32 = (float)((short)((audiobytes[0] << 8) | audiobytes[1]))/32768f;
if (sample32 > MaxValue)
return true;
}
return false;
}
Here is an example that moves the WaveIn logic to a background thread. It should give you enough to start. Please check the documentation for a complete example that includes the background thread cancellation.
//CLAP
private float bigValue;
WaveIn waveIn;
private double MaxValue;
private BackgroundWorker worker;
private void button1_Loaded(object sender, RoutedEventArgs e)
{
if (Convert.ToInt16(textBox1.Text) > 100)
{
MessageBox.Show("Invalid Value");
return;
}
else
MaxValue = Convert.ToDouble(textBox1.Text) / 100;
bigValue = 0;
// You'll need to handle the thread cancellation
// when the user clicks the button again
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += (s, e) =>
{
waveIn = new WaveIn();
int waveInDevices = waveIn.DeviceNumber;
//Get Device Count
for ( int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
{
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
}
waveIn.DeviceNumber = 0;
waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
int sampleRate = 8000;
int channels = 1;
waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
waveIn.StartRecording();
};
worker.ProgressChanged += (s, e) =>
{
SendMessage(MONITOR_ON, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON);
MessageBox.Show("Did you Clap?");
};
worker.RunWorkerAsync();
}
//CLAP
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
for (int index = 0; index < e.BytesRecorded; index += 2)
{
short sample = (short)((e.Buffer[index + 1] << 8) | e.Buffer[index + 0]);
float sample32 = sample / 32768f;
label1.Content = sample32.ToString();
if (bigValue < sample32)
{
bigValue = sample32;
label2.Content = bigValue.ToString();
if (bigValue > MaxValue)
{
worker.ReportProgress(0);
break;
}
}
}
}
So in the end I went with a different way than both suggested answers.
private float bigValue;
WaveIn waveIn;
private double MaxValue;
private void button1_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (Convert.ToInt16(textBox1.Text) > 100)
{
MessageBox.Show("Invalid Value");
return;
}
else
MaxValue = Convert.ToDouble(textBox1.Text) / 100;
bigValue = 0;
waveIn = new WaveIn();
int waveInDevices = waveIn.DeviceNumber;
//Get Device Count
for (int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
{
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
}
waveIn.DeviceNumber = 0;
waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
int sampleRate = 8000;
int channels = 1;
waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
waveIn.StartRecording();
}
private void button1_Loaded(object sender, RoutedEventArgs e)
{
if (Convert.ToInt16(textBox1.Text) > 100)
{
MessageBox.Show("Invalid Value");
return;
}
else
MaxValue = Convert.ToDouble(textBox1.Text) / 100;
bigValue = 0;
waveIn = new WaveIn();
int waveInDevices = waveIn.DeviceNumber;
for (int i = 0; i <= 100; i++)
{
}
//Get Device Count
for (int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
{
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
}
waveIn.DeviceNumber = 0;
waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
int sampleRate = 8000;
int channels = 1;
waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
waveIn.StartRecording();
}
int i = 0;
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
for (int index = 0; index < e.BytesRecorded; index += 2)
{
short sample = (short)((e.Buffer[index + 1] << 8) | e.Buffer[index + 0]);
float sample32 = sample / 32768f;
label1.Content = sample32.ToString();
if (bigValue < sample32)
{
bigValue = sample32;
label2.Content = bigValue.ToString();
if (bigValue > MaxValue)
{
waveIn.StopRecording();
if (IsOdd(i))
{
button1.IsEnabled = false;
}
else
{
button1.IsEnabled = true;
}
MessageBox.Show("Did you Clap?");
i++;
}
}
}
}
public static bool IsOdd(int value)
{
return value % 2 != 0;
}
}
The first load event sets it off. The second one goes back in forth between button on and button off using the IsEnabled event. The on and off are acheived by and if statement choosing between an odd number and even.
That is how I achieved this infinite loop.
NOTE: This way probably isn't the most efficient way but it got the job done.
Also I left the (Open Window) code out of this answer.