Making a c# program go back to a point and carry on - c#

I have a list of code that is implemented from a button click load event in one method. I also have a Mouse Event that returns the e.X and e.Y values. MY problem is that the program runs through the button click load event and I can see the mouse events but I do not know how to return the program to run it again from a position with the new e.X and e.Y values. I have tried to separate it into various methods but I cannot seem to be able to 'call' them or 'goto' them. Any advice on what I need to do? The code is listed below.
Many thanks Steve
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.IO;
using System.Runtime.InteropServices;
namespace HPXSV8
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int curX;
private int curY;
//private double ll;
//private double lr;
//private double lt;
//private double lb;
//private double GPSLat;
//private double GPSLong;
public int allimages;
public void Form1_Load(object sender, EventArgs e)
{
}
public void btnLoadFiles_Click(object sender, EventArgs e)
{
//Loads the data file syvwname
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Load Survey View Data File";
ofd.Filter = "Survey View File |*.syvw";
if (ofd.ShowDialog() == DialogResult.OK)
{
string syvwname = ofd.FileName;
string sfn = ofd.SafeFileName;
string[] loaddata = new string[500];
int allimages = 0;
string line;
StreamReader file = new StreamReader(syvwname);
List<string> list = new List<string>();
while ((line = file.ReadLine()) != null)
{
listBox1.Items.Add(line);
loaddata[allimages] = line;
allimages++;
}
string[,] gpsdata = new string[allimages, 3];
int z = 0;
do
{
//slices up the .syvw file into 3 string values comma delimited
string chop1 = loaddata[z];
string[] part = chop1.Split(',');
string[] parts = part.ToArray();
gpsdata[z, 0] = parts[0];
gpsdata[z, 1] = parts[1];
gpsdata[z, 2] = parts[2];
z++;
}
while (z < allimages);
//load jpg 0001 to picture box
int strlength = syvwname.Length;
int sfnamelength = sfn.Length;
string mainimage = gpsdata[0, 0];
string trimname = syvwname.Remove(strlength - sfnamelength, sfnamelength);
string filelocation = (trimname + #"\" + mainimage);
pictureBox1.ImageLocation = filelocation;
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
//Calculate values of lat and long to compare with image values
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double picboxX = 800.00; ////check these values in final program size
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//Calculate fraction position of cursor to picture
double curposX = curX / picboxX;
//+++++++++++++++++++++++++++++++++++++++++++++++++
label1.Text = "CurposX = " + curposX.ToString();
//+++++++++++++++++++++++++++++++++++++++++++++++++
//Calculate values of lat and long to compare with image values
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double picboxY = 600.00;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//Calculate fraction position of cursor to picture
double curposY = curY / picboxY;
//+++++++++++++++++++++++++++++++++++++++++++++++++
label2.Text = "CurposY =" + curposY.ToString(); // Test Line
//+++++++++++++++++++++++++++++++++++++++++++++++++
/// Calculate GPSLongditude from mouse positions
double ll = Convert.ToDouble(gpsdata[2, 1]); //??????????????????????????????
double lr = Convert.ToDouble(gpsdata[2, 2]);
//GPS Postion calculations based on picture box mouse position
//Longitude
double GPSLong = ll + ((lr - ll) * curposX);
//+++++++++++++++++++++++++++++++++++++++++++++++++
label3.Text = "GpsLong =" + GPSLong.ToString(); /// Test Line
//+++++++++++++++++++++++++++++++++++++++++++++++++
// Calculate GPSLatitude from mouse positions
double lb = Convert.ToDouble(gpsdata[1, 2]);
double lt = Convert.ToDouble(gpsdata[1, 1]);
//GPS Postion calculations based on picture box mouse position
//Latitude
double GPSLat = lt + ((lb - lt) * curposY);
//Look for values in my_datatable that fall within +/- 0.0005 of GPS Lat and Long
// This may be made to be a variable to increase the number of images
//=================================================================================
//Look for values in gpsdata[]
int row = 3;
int chs = 0;
string[] chosenimage = new string[allimages]; // value if all images are selected
//=================================================================================
double area = 0.0005; //later make this a UI variable
//=================================================================================
do
{
//make 4 values +/- 0.0005 of gpsdata[]
if (GPSLat <= (Convert.ToDouble(gpsdata[row, 1]) + area))
{
if (GPSLat >= (Convert.ToDouble(gpsdata[row, 1]) - area))
{
if (GPSLong <= (Convert.ToDouble(gpsdata[row, 2]) + area))
{
if (GPSLong >= (Convert.ToDouble(gpsdata[row, 2]) - area))
{
chosenimage[chs] = gpsdata[row, 0];
chs++;
}
}
}
}
row++;
}
while (row < allimages);
//+++++++++++++++++++++++++++++++++++++++++++++++++
label4.Text = "GpsLat = " + GPSLat.ToString();
//+++++++++++++++++++++++++++++++++++++++++++++++++
while ((line = file.ReadLine()) != null)
{
listBox2.Items.Add(line);
chosenimage[chs] = line;
allimages++;
}
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
public void btnSelect_Click(object sender, EventArgs e)
{
// Use to select images to use for notes
//label1.Text = insertnote.Text;
}
public void insertnote_MouseDown(object sender, MouseEventArgs e)
{
//Use for entering text into notepad area
}
public void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
//Get location of mouse click on 001.jpg
int curX = e.X;
int curY = e.Y;
//+++++++++++++++++++++++++++++++++++++++++++++++++
label1.Text = "X = " + curY.ToString() + " ; Y =" + curY.ToString(); /// Test Line
//+++++++++++++++++++++++++++++++++++++++++++++++++
}
}
} // End of Namespace HPXSV8

I hope I'm understanding your question. You have two events. You want Event A to update the X and Y values and the other Event B to do some calculation based on those values. If Event A occurs during the calculation, you want to redo the calculation.
I'll have to check up on this later, but I'm sure that the events you wrote are blocking. Meaning Event A has to complete before it can proceed with Event B. Additionally, Event B has to complete before it can proceed with Event A. This is why it won't work.
You could try doing this with a worker thread. Event B signals the thread to perform calculations. Event A will tell the worker thread that a value is invalid and has to redo a calculation.

Related

SheetChange event is not getting changes of active excel cell when performing another function

I am coding simple application having one window form using c# in visual studio, app exe will be opened by excel VBA macro and the application will read the value of activeworkbook.activesheet.cell(1,1) at startup and when any change occur to active sheet.
the form have the following controls:
label control name (label1)
ZEDGraphpan name (zedGraphControl1)
In c# code there are two commands
ReadExcel();
PlotGraph();
Readexcel: to update label1 text equal to ActiveWorkbook.ActiveSheet.cell(1,1).value on startup and on any change event in activesheet
PlotGraph: to generate multiple curves in ZEDGRAPH
The Issue:
If the code have Readexcel() alone it is working fine and updating label.text at start up and at every change of active sheet.
If code include both of Readexcel() and PlotGraph() together the application will update label1.text only at startup, it will not pick any change in activesheet hence not updating label1 in real-time.
The Question: how can I get the Readexcel() to pick the live changes in activesheet along with PlotGrah in the same code
the Code of the App is below
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows.Forms;
using ZedGraph;
using xl = Microsoft.Office.Interop.Excel;
namespace TestLiveZed
{
public partial class GraphForm : Form
{
public object ZedGraphcontrol1 { get; private set; }
public GraphForm()
{
InitializeComponent();
PlotGraph();
ReadExcel();
}
private void OWB_SheetChange(Object Sh, Range Target)
{
UpdateText(Target.Value.ToString());
}
private delegate void UpdateProgDelegate(String uptxt);
private void UpdateText(String uptxt)
{
if (this.label1.InvokeRequired)
{
UpdateProgDelegate updateCallBack = new UpdateProgDelegate(UpdateText);
this.Invoke(updateCallBack, new object[] { uptxt });
}
else
{
label1.Text = uptxt.ToString();
}
}
private void ReadExcel()
{
xl.Application oXL = (xl.Application)Marshal.GetActiveObject("Excel.Application");
Workbook oWB;
Worksheet oSheet;
oWB = oXL.ActiveWorkbook;
oSheet = oWB.ActiveSheet;
oWB.SheetChange += OWB_SheetChange;
label1.Text = Convert.ToString(oSheet.Cells[1, 1].Value);
}
private void PlotGraph()
{
GraphPane myPane = zedGraphControl1.GraphPane;
myPane.Title.Text = "Company A VS B";
myPane.XAxis.Title.Text = "r/D";
myPane.YAxis.Title.Text = "K";
string[] clr = { "Black", "Green", "Blue", "Orange", "Yellow", "Black", "Cyan", "DarkCyan", "Aqua", "Purple", "Pink", "Red", "AliceBlue", "Grey", "DarkGreen" };
int tr = 0;
for (int y = 0; y <= 180; y = y+15)
{
AddCurv(y,tr, clr);
tr++;
}
}
private void AddCurv(int y,int tr,string[] clr)
{
PointPairList CompAList = new PointPairList();
for (double i = 0.5; i < 16; i = i + 0.2)
{
double f = 0.02;
int Beta = y;
double Rratio = i;
double C = Math.Sin(0.5 * Beta * Math.PI / 180);
double AL = Beta * Math.PI / 180;
double r = (Math.Pow(C, 0.5) + C) / Math.Pow(Rratio, 4 * AL / Math.PI);
double K = f * AL * Rratio + (0.10 + 2.4 * f) * C + (6.6 * f * r);
CompAList.Add(i, K);
//CompBList.Add(i + 1, ComBData[i]);
};
string vclr = clr[tr];
_ = zedGraphControl1.GraphPane.AddCurve("Angle = " + y.ToString(), CompAList, Color.FromName(vclr), SymbolType.None);
zedGraphControl1.GraphPane.YAxis.Scale.Min = 0;
zedGraphControl1.GraphPane.YAxis.Scale.Max = 1;
zedGraphControl1.GraphPane.XAxis.Scale.Min = 0;
zedGraphControl1.GraphPane.XAxis.Scale.Max = 16;
zedGraphControl1.AxisChange();
zedGraphControl1.Refresh();
}
}
}
Finally I managed to make it works
Set the form events on activated and on deactivated
to run the void Readexcel();
private void Form1_Activated(object sender, EventArgs e)
{
ReadExcel();
}
private void Form1_Deactivate(object sender, EventArgs e)
{
ReadExcel();
}

Emgu.CV Object Marker

I built a pan-tilt system with 2 servos and laser diode. The system was controlled by Arduino Nano. By using Emgu.CV in C#, I created a grayscale image and convert it to a black-white image using GrayImg.ThresholdBinaryInv. I'm using Inverse Kinematics calculations but the laser diode couldn't mark the object well. Where did I make a mistake? Image coordinate system and servos coordinate systems are not same. Thus, I did mapping and scaling. It should work but the accuracy of the laser diode is inaccurate.
Arduino Code
#include <Servo.h>
int Th1, Th2, tmp;
Servo M1;
Servo M2;
void setup()
{
Serial.begin(9600);
pinMode(6,OUTPUT; // Laser Diode
digitalWrite(6,0);
Th1 = 0;
Th2 = 0;
M1.attach(3);
M1.write(90);
M2.attach(9);
M2.write(90);
}
void loop()
{
delay(200); //sync issue only
if(Serial.available()>=2)
{
Th1 = Serial.read(); //read only one byte
Th2 = Serial.read();
M1.write(Th1);
M2.write(Th2);
//Remove any extra worng reading
while(Serial.available()) tmp = Serial.read();
// Run the robotic arm here. For testing, we will
digitalWrite(6,1);
delay(500);
digitalWrite(6,0);
delay(500);
//switch On or switch off a LED according to Th1 value
//Serial.print('1'); // This tell the PC that Arduino is Ready for next angles
}
}
C# Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
namespace ObjectMarker
{
public partial class Form1 : Form
{
private Capture capture;
private Image<Bgr, Byte> IMG;
private Image<Gray,Byte> blackWhite;
private Image<Gray, Byte> GrayImg;
private int N, Xpx, Ypx; // N -> Number of non-black pixels
private double Xcm, Ycm;
private double myScale;
private double Th1, Th2;
static SerialPort _serialPort;
public byte []Buff = new byte[1];
public Form1() //Constructor of the Form1
{
InitializeComponent();
//myScale = 65.0 / 480.0;
myScale = 1.7 / 13;
_serialPort = new SerialPort();
_serialPort.PortName = "COM4";//Set your board COM
_serialPort.BaudRate = 9600;
_serialPort.Open();
}
private void processFrame(object sender, EventArgs e) // Most important function
{ //You're not connected any camera - null
if (capture == null)//very important to handel excption - capture - point of the camera
{
try
{
capture = new Capture(1); //creatine a object
}
catch (NullReferenceException excpt)
{
MessageBox.Show(excpt.Message);
}
}
IMG = capture.QueryFrame();// capture the current frame. Get an image.
GrayImg = IMG.Convert<Gray, Byte>();
blackWhite = GrayImg.ThresholdBinaryInv(new Gray(25),new Gray (255));
Xpx = 0;
Ypx = 0;
N = 0;
for (int i = 0; i < blackWhite.Width; i++) {
for (int j = 0; j < blackWhite.Height; j++) {
if(blackWhite[j,i].Intensity > 128){
N++;
Xpx += i;
Ypx += j;
}
}
}
if(N>0){
Xpx = Xpx / N;
Ypx = Ypx / N;
Xpx = (blackWhite.Width / 2) - Xpx; //320 - xpx
Ypx = Ypx - (blackWhite.Height / 2); // ypx - 240
Xcm = Xpx * myScale;
Ycm = Ypx * myScale;
double d3 = 28; // Laser to wall dist. (?)
double Zcm = 108; // Cam to wall dist.
double d1 = 3.50; // Joint to joint dist.
double l2 = 4.50; // Joint to laser dist. (?)
textBox1.Text = Xcm.ToString();
textBox2.Text = Ycm.ToString();
textBox3.Text = N.ToString();
textBox11.Text = Zcm.ToString();
textBox12.Text = myScale.ToString();
//Inverse Calculations
double Px, Py, Pz,Diff = 0;
// Mapping
//Px = Zcm;
Px = -1 * Zcm;
Py = -1 * Xcm;
Pz = Ycm + Diff; // The laptop has built-in camera and the pan-tilt above the keyboard so there's a distance between camera and pan-tilt system (laser diode)
textBox8.Text = Px.ToString();
textBox9.Text = Py.ToString();
textBox10.Text = Pz.ToString();
Th1 = Math.Atan((Py / Px));
Th2 = Math.Atan((Math.Sin(Th1) * (Pz - d1)) / Py);
//Th1 = Math.Atan(Ycm / Xcm);
//Th2 = Math.Atan((Math.Sin(Th1) * (Zcm - d1)) / Ycm);
textBox4.Text = Th1.ToString();
textBox5.Text = Th2.ToString();
Th1 = (Th1 * (180 / Math.PI));
Th2 = (Th2 * (180 / Math.PI));
Th1 += 90;
Th2 += 90;
textBox6.Text = Th1.ToString();
textBox7.Text = Th2.ToString();
label11.Text = trackBar1.Value.ToString();
label12.Text = trackBar2.Value.ToString();
Buff[0] = (byte)trackBar1.Value; //Th1
Buff[2] = (byte)trackBar2.Value; //Th2
_serialPort.Write(Buff,0,2);
}
else
{
textBox1.Text = "";
textBox2.Text = "";
textBox3.Text = N.ToString();
Buff[0] = (byte)90; //Th1
Buff[2] = (byte)90; //Th2
_serialPort.Write(Buff, 0, 2);
}
try
{
imageBox1.Image = IMG;
imageBox2.Image = GrayImg;
imageBox3.Image = blackWhite;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void button1_Click(object sender, EventArgs e)
{
//Application.Idle += processFrame; //Serial comm between arduino and computer
timer1.Enabled = true;
button1.Enabled = false;
button2.Enabled = true;
}
private void button2_Click(object sender, EventArgs e)
{
//Application.Idle -= processFrame;
timer1.Enabled = false;
button1.Enabled = true;
button2.Enabled = false;
}
private void button3_Click(object sender, EventArgs e) // I measure the object size from paint. It gave me pixel size. Then I measure the real size of the image. Real size divided by pixel size gives scale.
{
IMG.Save("G:\\CurrentFrame" + ".jpg");
}
void Timer1Tick(object sender, EventArgs e)
{
processFrame(sender,e);
}
}
}
EDIT: Images has been added. The problem is if I change the tape location inaccuracy of the system isn't accurate. In some points laser marked the tape very well but mostly on corners there's huge difference between laser and tape.
https://i.stack.imgur.com/fmm1x.png
https://i.stack.imgur.com/Quadr.png
https://i.stack.imgur.com/9SWa4.png
This question is very hard to answer, as there are many places where an error could have crept in. Without further diagnosis and knowledge of the system, it's almost impossible to give a clear answer. I suggest you perform some more diagnosis, and if the answer still eludes you, reformulate your question (or close it when you find the answer yourself).
Some possible sources of error (and points for diagnosis) could be:
In the Arduino code, you throw away anything after the first two bytes of Serial communication. Why would there be extra bytes? Are you sure the extra bytes are garbage, and, inversely, the first two bytes are the correct ones?
In the C# mapping of coordinates, you perform a translation of the coordinates. Are you sure the system is exactly axis-parallel? An error of just a few degrees quickly adds up. You might need to add a rotational transformation.
Your C# code returns an angle (in degrees) for the servo. This could be negative, or above 255, so converting it to a byte could, on overflow, lead to passing a (completely) wrong value to the Arduino.

How to move all controls on a form based on mouse movement at runtime

I am having a bit of a small issue performing a certain task in my winforms application.
I am basically attempting to recreate a "Top-View RTS Map", on a winform. In order to save memory, not all tiles of the "Map" are displayed on the screen. Only the ones that fit within the viewport. Therefore, I am trying to allow the user to perform a pan/scroll on the displayed tiles in order to navigate the entire map!
Right now, I am doing this by creating and displaying GroupBox controls dynamically at runtime. These represent the tiles...
I have created my own objects to support all of this (contains screen coordinates, Row and Col info, etc.)
Here is how I am currently accomplishing all of this, in pseudo-code:
Creating the form, tiles and the map in general
I create a winforms form that is 600px X 600px.
I create a new "Map" (using a List<MapTile>) that is 100 tiles by 100 tiles (for testing) on form load and save that into a variable.
I keep track of the displayed tiles via another list (or property that derives from the main list bool MapTile.isDrawn)
Each tile is visually made of a GroupBox control that is 100px
X 100px (so [7 X 7] of them fit on screen)
To start, I find the center MapTile (tile [50, 50]) in the "Map", create the GroupBox for it and place that in the middle of the form,
I then add the other tiles/controls necessary to fill in the form (center - 3 tiles, center + 3 tiles (up, down, left, and right)).
Each tile, of course, subscribes to the proper mouse events to perform a drag
When the user mouse drags a tile, all other tiles being displayed follow suit/follow the leader by updating all "displayed tiles" coordinates to match the movement that was made by the "dragged" tile.
Managing Displayed Tiles
While the GroupBox tiles are being dragged/moved, I perform a check to see if the tiles that are on the outer edge of the viewport are within its bounds.
If, as an example, the top-left-most tile's right edge falls outside the bounds of the left edge of the viewport, I remove the entire left column tiles, and add the entire right column tiles programmatically. The same goes all directions (up, down, left and right).
So far, this works fine as long as I don't go too fast... however, when I drag the tiles "too fast" passed an outer edge (e.g.: where point 2 ci-dessus would apply), it seems that the application is unable to keep up because it doesn't add the column or row where they should be on the form, and other times, it does not have time to remove all controls of a row or column and I end up with controls that are still on the screen when they shouldn't be there. At that point the entire grid/map is off balance and stops working as intended because either the events that should fire on one edge don't (the tiles are not present) and/or there are now multiple controls with the same name on the form and the removal or referencing fails...
While I am well aware that winforms is not designed to perform intensive GPU/GDI operations, you would think that something this simple would still be easily do-able in winforms?
How would I go about making this more responsive at runtime? Here's my entire set of code:
Form code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RTSAttempt
{
public enum DrawChange
{
None,
Rem_First_Draw_Last,
Rem_Last_Draw_First
};
public partial class Form1 : Form
{
public string selected { get; set; }
private int _xPos { get; set; }
private int _yPos { get; set; }
private bool _dragging { get; set; }
public List<MapTile> mapTiles { get; set; }
public List<MapTile> drawnTiles { get { return this.mapTiles.Where(a => a.Drawn == true).ToList(); } }
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//init globals
this.selected = "";
this._dragging = false;
this.mapTiles = new List<MapTile>();
//for testing, let's do 100 x 100 map
for (int i = 0; i < 100; i++)
{
for (int x = 0; x < 100; x++)
{
MapTile tile = new MapTile(x, i, false, -1, -1, false);
this.mapTiles.Add(tile);
}
}
GenerateStartupTiles();
}
/// <summary>
/// Used to generate the first set of map tiles on screen and dispaly them.
/// </summary>
private void GenerateStartupTiles()
{
//find center tile based on list size
double center = Math.Sqrt(this.mapTiles.Count);
//if not an even number of map tiles, we take the next one after the root.
if (this.mapTiles.Count % 2 != 0)
center += 1;
//now that we have the root, we divide by 2 to get the true center tile.
center = center / 2;
//get range of tiles to display...
int startat = (int)center - 3;
int endat = (int)center + 3;
//because the screen is roughly 600 by 600, we can display 7 X 7 tiles...
for (int row = 0; row < 7; row++)
{
for (int col = 0; col < 7; col++)
{
//get the current tile we are trying to display.
MapTile tile = mapTiles.First(a => a.Row == (startat + row) && a.Col == (startat + col));
//create and define the GroupBox control we use to display the tile on screen.
GroupBox pct = new GroupBox();
pct.Width = 100;
pct.Height = 100;
//find start position on screen
if (row == 0)
pct.Top = -50;
else
pct.Top = -50 + (row * 100);
if (col == 0)
pct.Left = -50;
else
pct.Left = -50 + (col * 100);
tile.X = pct.Left;
tile.Y = pct.Top;
pct.Name = tile.ID;
pct.Tag = Color.LightGray;
//subscribe to necessary events.
pct.MouseEnter += Pct_MouseEnter;
pct.MouseLeave += Pct_MouseLeave;
pct.Click += Pct_Click;
pct.Paint += Pct_Paint;
pct.MouseDown += Pct_MouseDown;
pct.MouseMove += Pct_MouseMove;
pct.MouseUp += Pct_MouseUp;
pct.Text = tile.DisplayID;
//add the tile to the screen
this.Controls.Add(pct);
//set the tile to Drawn mode...
tile.Drawn = true;
}
}
}
private void Pct_MouseUp(object sender, MouseEventArgs e)
{
//self explanatory
if (this._dragging)
{
Cursor.Current = Cursors.Default;
this._dragging = false;
}
}
private void Pct_MouseMove(object sender, MouseEventArgs e)
{
var c = sender as GroupBox;
if (!_dragging || null == c) return;
//get original position, and movement step/distance for calcs.
int newTop = e.Y + c.Top - _yPos;
int newLeft = e.X + c.Left - _xPos;
int movedByX = this.drawnTiles.First(a => a.ID.ToString() == c.Name).X;
int movedByY = this.drawnTiles.First(a => a.ID.ToString() == c.Name).Y;
movedByY = newTop - movedByY;
movedByX = newLeft - movedByX;
//perform all tile movements here
MoveAllTiles(movedByX, movedByY);
}
/// <summary>
/// This method performs all tile movements on screen, and updates the listing properly.
/// </summary>
/// <param name="X">int - the amount fo pixels that the dragged tile has moved horizontally</param>
/// <param name="Y">int - the amount fo pixels that the dragged tile has moved vertically</param>
private void MoveAllTiles(int X, int Y)
{
//used to single out the operation, if any, that we need to do after this move (remove row or col, from edges)
DrawChange colAction = DrawChange.None;
DrawChange rowAction = DrawChange.None;
//move all tiles currently being displayed first...
for (int i = 0; i < this.drawnTiles.Count; i++)
{
//first, determine new coordinates of tile.
drawnTiles[i].Y = drawnTiles[i].Y + Y;
drawnTiles[i].X = drawnTiles[i].X + X;
//find the control
GroupBox tmp = this.Controls.Find(drawnTiles[i].ID, true)[0] as GroupBox;
//perform screen move
tmp.Top = drawnTiles[i].Y;
tmp.Left = drawnTiles[i].X;
tmp.Refresh();
}
//dtermine which action to perform, if any...
if (drawnTiles.Last().Y > this.Height)
rowAction = DrawChange.Rem_Last_Draw_First;
else if ((drawnTiles.First().Y + 100) < 0)
rowAction = DrawChange.Rem_First_Draw_Last;
else
rowAction = DrawChange.None;
if ((drawnTiles.First().X + 100) < 0)
colAction = DrawChange.Rem_First_Draw_Last;
else if (drawnTiles.Last().X > this.Width)
colAction = DrawChange.Rem_Last_Draw_First;
else
colAction = DrawChange.None;
//get currently dispalyed tile range.
int startRow = this.drawnTiles.First().Row;
int startCol = this.drawnTiles.First().Col;
int endRow = this.drawnTiles.Last().Row;
int endCol = this.drawnTiles.Last().Col;
//perform the correct action(s), if necessary.
if (rowAction == DrawChange.Rem_First_Draw_Last)
{
//remove the first row of tiles from the screen
this.drawnTiles.Where(a => a.Row == startRow).ToList().ForEach(a => { a.Drawn = false; this.Controls.RemoveByKey(a.ID); this.Refresh(); });
//add the last row of tiles on screen...
List<MapTile> TilesToAdd = this.mapTiles.Where(a => a.Row == endRow + 1 && a.Col >= startCol && a.Col <= endCol).ToList();
int newTop = this.drawnTiles.Last().Y + 100;
for (int i = 0; i < TilesToAdd.Count; i++)
{
int newLeft = (i == 0 ? drawnTiles.First().X : drawnTiles.First().X + (i * 100));
//create and add the new tile, and set it to Drawn = true.
GroupBox pct = new GroupBox();
pct.Name = TilesToAdd[i].ID.ToString();
pct.Width = 100;
pct.Height = 100;
pct.Top = newTop;
TilesToAdd[i].Y = newTop;
pct.Left = newLeft;
TilesToAdd[i].X = newLeft;
pct.Tag = Color.LightGray;
pct.MouseEnter += Pct_MouseEnter;
pct.MouseLeave += Pct_MouseLeave;
pct.Click += Pct_Click;
pct.Paint += Pct_Paint;
pct.MouseDown += Pct_MouseDown;
pct.MouseMove += Pct_MouseMove;
pct.MouseUp += Pct_MouseUp;
pct.Text = TilesToAdd[i].DisplayID;
this.Controls.Add(pct);
TilesToAdd[i].Drawn = true;
}
}
else if (rowAction == DrawChange.Rem_Last_Draw_First)
{
//remove last row of tiles
this.drawnTiles.Where(a => a.Row == endRow).ToList().ForEach(a => { a.Drawn = false; this.Controls.RemoveByKey(a.ID); this.Refresh(); });
//add first row of tiles
List<MapTile> TilesToAdd = this.mapTiles.Where(a => a.Row == startRow - 1 && a.Col >= startCol && a.Col <= endCol).ToList();
int newTop = this.drawnTiles.First().Y - 100;
for (int i = 0; i < TilesToAdd.Count; i++)
{
int newLeft = (i == 0 ? drawnTiles.First().X : drawnTiles.First().X + (i * 100));
//create and add the new tile, and set it to Drawn = true.
GroupBox pct = new GroupBox();
pct.Name = TilesToAdd[i].ID.ToString();
pct.Width = 100;
pct.Height = 100;
pct.Top = newTop;
TilesToAdd[i].Y = newTop;
pct.Left = newLeft;
TilesToAdd[i].X = newLeft;
pct.Tag = Color.LightGray;
pct.MouseEnter += Pct_MouseEnter;
pct.MouseLeave += Pct_MouseLeave;
pct.Click += Pct_Click;
pct.Paint += Pct_Paint;
pct.MouseDown += Pct_MouseDown;
pct.MouseMove += Pct_MouseMove;
pct.MouseUp += Pct_MouseUp;
pct.Text = TilesToAdd[i].DisplayID;
this.Controls.Add(pct);
TilesToAdd[i].Drawn = true;
}
}
if (colAction == DrawChange.Rem_First_Draw_Last)
{
//remove the first column of tiles
this.drawnTiles.Where(a => a.Col == startCol).ToList().ForEach(a => { a.Drawn = false; this.Controls.RemoveByKey(a.ID); this.Refresh(); });
//add the last column of tiles
List<MapTile> TilesToAdd = this.mapTiles.Where(a => a.Col == endCol + 1 && a.Row >= startRow && a.Row <= endRow).ToList();
int newLeft = this.drawnTiles.Last().X + 100;
for (int i = 0; i < TilesToAdd.Count; i++)
{
int newTop = (i == 0 ? drawnTiles.First().Y : drawnTiles.First().Y + (i * 100));
//create and add the new tile, and set it to Drawn = true.
GroupBox pct = new GroupBox();
pct.Name = TilesToAdd[i].ID.ToString();
pct.Width = 100;
pct.Height = 100;
pct.Top = newTop;
TilesToAdd[i].Y = newTop;
pct.Left = newLeft;
TilesToAdd[i].X = newLeft;
pct.Tag = Color.LightGray;
pct.MouseEnter += Pct_MouseEnter;
pct.MouseLeave += Pct_MouseLeave;
pct.Click += Pct_Click;
pct.Paint += Pct_Paint;
pct.MouseDown += Pct_MouseDown;
pct.MouseMove += Pct_MouseMove;
pct.MouseUp += Pct_MouseUp;
pct.Text = TilesToAdd[i].DisplayID;
this.Controls.Add(pct);
TilesToAdd[i].Drawn = true;
}
}
else if (colAction == DrawChange.Rem_Last_Draw_First)
{
//remove last column of tiles
this.drawnTiles.Where(a => a.Col == endCol).ToList().ForEach(a => { a.Drawn = false; this.Controls.RemoveByKey(a.ID); this.Refresh(); });
//add first column of tiles
List<MapTile> TilesToAdd = this.mapTiles.Where(a => a.Col == startCol - 1 && a.Row >= startRow && a.Row <= endRow).ToList();
int newLeft = this.drawnTiles.First().X - 100;
for (int i = 0; i < TilesToAdd.Count; i++)
{
int newTop = (i == 0 ? drawnTiles.First().Y : drawnTiles.First().Y + (i * 100));
//create and add the new tile, and set it to Drawn = true.
GroupBox pct = new GroupBox();
pct.Name = TilesToAdd[i].ID.ToString();
pct.Width = 100;
pct.Height = 100;
pct.Top = newTop;
TilesToAdd[i].Y = newTop;
pct.Left = newLeft;
TilesToAdd[i].X = newLeft;
pct.Tag = Color.LightGray;
pct.MouseEnter += Pct_MouseEnter;
pct.MouseLeave += Pct_MouseLeave;
pct.Click += Pct_Click;
pct.Paint += Pct_Paint;
pct.MouseDown += Pct_MouseDown;
pct.MouseMove += Pct_MouseMove;
pct.MouseUp += Pct_MouseUp;
ToolTip tt = new ToolTip();
tt.SetToolTip(pct, pct.Name);
pct.Text = TilesToAdd[i].DisplayID;
this.Controls.Add(pct);
TilesToAdd[i].Drawn = true;
}
}
}
private void Pct_MouseDown(object sender, MouseEventArgs e)
{
//self explanatory
if (e.Button != MouseButtons.Left) return;
_dragging = true;
_xPos = e.X;
_yPos = e.Y;
}
private void Pct_Click(object sender, EventArgs e)
{
//changes the border color to reflect the selected tile...
if (!String.IsNullOrWhiteSpace(selected))
{
if (this.Controls.Find(selected, true).Length > 0)
{
GroupBox tmp = this.Controls.Find(selected, true)[0] as GroupBox;
ControlPaint.DrawBorder(tmp.CreateGraphics(), tmp.ClientRectangle, Color.LightGray, ButtonBorderStyle.Solid);
}
}
GroupBox pct = sender as GroupBox;
ControlPaint.DrawBorder(pct.CreateGraphics(), pct.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
this.selected = pct.Name;
}
private void Pct_Paint(object sender, PaintEventArgs e)
{
//draws the border based on the correct tag.
GroupBox pct = sender as GroupBox;
Color clr = (Color)pct.Tag;
ControlPaint.DrawBorder(e.Graphics, pct.ClientRectangle, clr, ButtonBorderStyle.Solid);
}
private void Pct_MouseLeave(object sender, EventArgs e)
{
//draws the border back to gray, only if this is not the selected tile...
GroupBox pct = sender as GroupBox;
if (this.selected != pct.Name)
{
pct.Tag = Color.LightGray;
pct.Refresh();
}
}
private void Pct_MouseEnter(object sender, EventArgs e)
{
//draws a red border around the tile to show which tile the mouse is currently hovering on...
GroupBox pct = sender as GroupBox;
pct.Tag = Color.Red;
pct.Refresh();
}
}
}
MapTile object
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RTSAttempt
{
public class MapTile
{
/// <summary>
/// Represents the row of the tile on the map
/// </summary>
public int Row { get; set; }
/// <summary>
/// Represents the column of the tile on the map
/// </summary>
public int Col { get; set; }
/// <summary>
/// Represents the ID of this tile ([-1,-1], [0,0], [1,1], etc
/// </summary>
public string ID { get { return "Tile_" + this.Row + "_" + this.Col; } }
public string DisplayID { get { return this.Row + ", " + this.Col; } }
/// <summary>
/// If this tile is currently selected or clicked.
/// </summary>
public bool Selected { get; set; }
/// <summary>
/// Represents the X screen coordinates of the tile
/// </summary>
public int X { get; set; }
/// <summary>
/// Represents the Y screen coordinates of the tile
/// </summary>
public int Y { get; set; }
/// <summary>
/// Represents whether this tile is currently being drawn on the screen.
/// </summary>
public bool Drawn { get; set; }
public MapTile(int idCol = -1, int idRow = -1, bool selected = false, int screenX = -1, int screenY = -1, bool drawn = false)
{
this.Col = idCol;
this.Row = idRow;
this.Selected = selected;
this.X = screenX;
this.Y = screenY;
this.Drawn = drawn;
}
public override bool Equals(object obj)
{
MapTile tmp = obj as MapTile;
if (tmp == null)
return false;
return this.ID == tmp.ID;
}
public override int GetHashCode()
{
return this.ID.GetHashCode();
}
}
}
I'd create the grid using (DataGridView, TableLayoutPanel, GDI+, or whatever) and then in the drag and drop, just calculate the new indexes and update the indexes, without moving the grid.
Example
The following example shows how to do it using a TableLayoutPanel:
Assign a fixed size to cells
Build the grid to fill the form
When form resizes, rebuild the grid
In mouse down capture the mouse down point and current top left index of grid
In mouse move, calculate the new index based on mouse movement and update index
In cell paint of the panel, draw the indexes
Here is the code:
int topIndex = 0, leftIndex = 0;
int originalLeftIndex = 0, originalTopIndex = 0;
int cellSize = 100;
Point p1;
TableLayoutPanel panel;
void LayoutGrid()
{
panel.SuspendLayout();
var columns = (ClientSize.Width / cellSize) + 1;
var rows = (ClientSize.Height / cellSize) + 1;
panel.RowCount = rows;
panel.ColumnCount = columns;
panel.ColumnStyles.Clear();
panel.RowStyles.Clear();
for (int i = 0; i < columns; i++)
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, cellSize));
for (int i = 0; i < rows; i++)
panel.RowStyles.Add(new RowStyle(SizeType.Absolute, cellSize));
panel.Width = columns * cellSize;
panel.Height = rows * cellSize;
panel.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single;
panel.ResumeLayout();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
panel = new MyGrid();
this.Controls.Add(panel);
LayoutGrid();
panel.MouseDown += Panel_MouseDown;
panel.MouseMove += Panel_MouseMove;
panel.CellPaint += Panel_CellPaint;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
if (panel != null)
LayoutGrid();
}
private void Panel_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
{
var g = e.Graphics;
TextRenderer.DrawText(g, $"({e.Column + leftIndex}, {e.Row + topIndex})",
panel.Font, e.CellBounds, panel.ForeColor);
}
private void Panel_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
var dx = (e.Location.X - p1.X) / cellSize;
var dy = (e.Location.Y - p1.Y) / cellSize;
leftIndex = originalLeftIndex - dx;
topIndex = originalTopIndex - dy;
panel.Invalidate();
}
}
private void Panel_MouseDown(object sender, MouseEventArgs e)
{
p1 = e.Location;
originalLeftIndex = leftIndex;
originalTopIndex = topIndex;
}
To prevent flicker:
public class MyGrid : TableLayoutPanel
{
public MyGrid()
{
DoubleBuffered = true;
}
}
So, for anyone trying to do this, as a concept, here is how to fix this issue:
Instead of only drawing 1 row/col extra outside the viewport to save memory, draw the entire viewport's worth of cells in every direction of the edges (up, down, left and right)... for example, if your viewport can hold 5 tiles (5 X 5 = 25), then you need to draw 5 X 5 outside the viewport in every other direction (25 X 4 = 100)...
When the mouse is being dragged, just move the controls that are already on the form/control/"drawn"... this way, the user cannot, while dragging, go outside the bounds of the existing tiles... example, if they reach the outer right edge with the mouse, while dragging the left-most tile, the tiles to show on the left already exist! So we are just "following the mouse", which is not an issue if the controls are already there/there is no "loss/issues" because we are not removing or adding any tiles at this point...
When the user stops dragging the selected tile around (onMouseUp), THEN we re-calculate the tiles that need to be drawn and those that don't... so we only redraw (add and/or remove controls where necessary) the entire set of "drawn" tiles after the user is done dragging...
With this method, you remove any "missplaced" controls, double generation of controls, controls missing and any other issue that arises when the mouse moves too fast for the "Calculate drawn tiles" code to execute. You also "see" the map moving around as you drag, and you always have the correct tiles drawn on the screen! Problem solved!
However, I did find that when I used a UserControl instead of the form itself, the controls draw and update much faster and better than if I just add them to the form itself... therefore, I have accepted the answer that outlines that aspect as the actual answer, and placed this here for anyone else in future that might wonder how to do this as a concept.

how do I paint one of the check when user click mouse on it

I have something like chessboard, 21x21 checks, each of one have 10x10pixels. I have no idea how do I paint one of the check when user click mouse on it.
Probably i have to use arrays but i dont know how can I assign 10x10 pixels to one element of other array. Can i assign array wiht 100 elements to one elemnet of other array?
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace gotowa_mapa
{
public partial class Form1 : Form
{
Bitmap mapa;
private int szer_pb = 200;
private int wys_pb = 200;
private int ilosc_kratek_x = 41;
private int ilosc_kratek_y = 41;
// private void wpisanie_tablic()
//{
// int[,] tablica_kratki = new int[ilosc_kratek_x, ilosc_kratek_y];
// int[,] tablica_piksele = new int[szer_pb, wys_pb];
//for(int i=0;i<ilosc_kratek_x; i++)
// {
// for(int j=0; j<ilosc_kratek_y; j++)
//{
// tablica_kratki[ilosc_kratek_x, ilosc_kratek_y] = tablica_piksele[2,2];
//}
//}
//}
private void siatka1()
{
mapa = new Bitmap(szer_pb, wys_pb);
int factor_x = (int)(szer_pb / ilosc_kratek_x);
int factor_y = (int)(wys_pb / ilosc_kratek_y);
for (int i = 0; i < szer_pb; i++)
{
if (i > (factor_x * ilosc_kratek_x))
break;
for (int j = 0; j < wys_pb; j++)
{
if (j > (factor_y * ilosc_kratek_y))
break;
if (i % (factor_x) == 0 || j % (factor_y) == 0)
{
mapa.SetPixel(i, j, Color.Black);
mapa.SetPixel(j, i, Color.Black);
}
else
{
mapa.SetPixel(i, j, Color.White);
mapa.SetPixel(j, i, Color.White);
}
}
}
}
// private void rysowanie_scian(int x, int y)
// {
// }
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
siatka1();
pictureBox1.Image = mapa;
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
int x, y;
x = e.X;
y = e.Y;
MessageBox.Show(x + " " + y);
}
}
}
I've made an example for you, although there is probably better ways of doing this, using some simple math you can achieve this.
A neat trick is overriding the onPaint method of a panel, making a new Bitmap with the same size as the panel, drawing this image over the panel during the onPaint method, making changes directly to the image, then invalidating the panel so it draws the image. (You can then save the image with Bitmap.Save)
Keep in mind, if a game of some sort is what you are aiming for, GDI is not ideal, look into managed DirectX or similar.
//declare variables
public const int offset = 50;
public const int boardX = 10;
public const int boardY = 10;
public const int checkSize = 40;
int[] checks;
//set initial values for the checks array
if(checks == null)
{
checks = new int[boardX * boardY];
}
//start iterating through the array of checks
for (int i = 0; i < checks.Length; i++)
{
//if the check is empty (assuming you will use integers as identifiers for the pieces on the board)
if (checks[i] < 1)
{
//check if the check in array is odd number & paint odd numbers black on alternating rows
if (i % 2 != 0)
{
//check if row is odd
if (Math.Ceiling((double)(i + 1) / boardX) % 2 != 0)
{
drawCheck(Brushes.Black, e.Graphics, i);
}
else
{
drawCheck(Brushes.White, e.Graphics, i)
}
}
else
{
//check if row is odd
if (Math.Ceiling((double)(i+1) / boardX) % 2 != 0;
{
drawCheck(Brushes.White, e.Graphics, i);
}
else
{
drawCheck(Brushes.Black, e.Graphics, i)
}
}
}
else if (checks[i] == 1)
{
//the check is a "clicked check" - draw it red
drawCheck(Brushes.Red, e.Graphics, i);
}
}
//The Methods
private void YOURDRAWINGSURFACE_MouseClick(object sender, MouseEventArgs e)
{
if(e.X < (boardX * checkSize) + offset && e.Y < (boardY * checkSize) + offset && e.X > offset && e.Y > offset )
{
//you have clicked somewhere on the board. if that square isn't red, make it so, else turn it back to a normal square
if (checks[checkClicked(e.X, e.Y)] != 1)
{
checks[checkClicked(e.X, e.Y)] = 1;
}
else
{
checks[checkClicked(e.X, e.Y)] = 0;
}
}
//this is only relevant for GDI drawing code. It will invalidate the form to redraw the surface (needs to be called whenever you make a change)
this.Invalidate();
}
public int checkClicked(int mouseX, int mouseY)
{
//this method returns the int of the particular check that was clicked in the array.
return ((int)Math.Ceiling((double)(mouseX - offset) / checkSize) + (int)((Math.Ceiling((double)(mouseY - offset) / checkSize)) * boardX) - (1 + boardX));
}
public void drawCheck(Brush brush, Graphics graphics, int checkNumber)
{
//this method draws the check given the parameters.
graphics.FillRectangle(brush, ((checkNumber % boardX) * checkSize) + offset, (int)(Math.Ceiling((double)(checkNumber-(boardX-1)) / boardX) * checkSize) + offset, checkSize, checkSize);
}

code improvement - naming textboxes generated form numericupdown & button click

I have a problem with a code that is regarding adding controls with numericUpDown ( for example- if numericUpDown value equals to 3, user recievs 3 textboxes).
Thanks to stackoverflow users I improved my code.
Before improvement it looked like this:
private void button1_Click(object sender, EventArgs e)
{
if (numericUpDown1.Value == 1)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
}
else if (numericUpDown1.Value == 2)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
txtbx2.AutoSize = true;
Controls.Add(txtbx2);
txtbx2.Location = new Point(70, 130);
}
else if (numericUpDown1.Value == 3)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
txtbx2.AutoSize = true;
Controls.Add(txtbx2);
txtbx2.Location = new Point(70, 130);
txtx3.AutoSize = true;
Controls.Add(txtbx3);
txtbx3.Location = new Point(70, 160);
}
}
After improvement:
private void button1_Click(object sender, EventArgs e)
{
int y = 100;
int x = 70;
for (int i = 0; i < numericUpDown1.Value; i++)
{
var txtbx = new TextBox();
txtbx.AutoSize = true;
Controls.Add(txtbx);
txtbx.Location = new Point(x, y);
// Increase the y-position for next textbox.
y += 30;
}
}
Now the problem is that I don't know how assign names to genarated textboxes.
(before the improvement I could name them - txtbx1, txtbx2, txtbx3...)
Code to improve:
private void button3_Click(object sender, EventArgs e)
{
try
{
double a, b, c, sum;
a = double.Parse(txtbx1.Text);
b = double.Parse(txtbx2.Text);
c = double.Parse(txtbx3.Text);
sum = a + b + c;
label1.Text = sum.ToString();
}
catch (Exception ex)
{
MessageBox.Show("error");
}
Please note that I'm a beginner, learning c# by watching youtube tutorials ;) I do realize that my question might be silly but I couldn't handle this problem by myself.
In advance thank you for your time and help.
If you need to access them afterwards, you have some options.
I'll guess that your objective is to set label1's text to the sum of the values contained in the specified textbox(es).
On the ValueChanged event of your NumericUpDown, check the delta and consequentely add or remove the required number of TextBoxes to your Form's Controls. To obtain the delta, you'll need to store the previous value of the NumericUpDown, and then subtract it from the current value. (If it was 5, and now it's 4, 4 - 5 = -1. A textbox has been removed).
private int _oldNUDvalue = 0; //or assign it to the default value
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
int delta = (int)numericUpDown1.Value - _oldNUDvalue;
if (delta < 0)
{
for (int i = -delta; i > 0; i--)
{
var tbox = Controls.Find("ntextBox" + (_oldNUDvalue - i), false)[0];
Controls.Remove(tbox);
}
}
else if (delta > 0)
{
for (int i = _oldNUDvalue; i < _oldNUDvalue + delta; i++)
{
var tbox = new TextBox();
tbox.Location = new Point(15, 55 + (30 * i));
tbox.Name = "ntextBox" + i;
Controls.Add(tbox);
}
}
_oldNUDvalue = (int)numericUpDown1.Value;
}
If, however, you only have a maximum number of 3, you could take a slightly different approach. My solution works with n-textboxes.
Finally, to get the TextBoxes' values from code, you have three approaches:
Loop through your Form's controls, check for TextBoxes with their name starting with "ntextBox", and add their values together
Use LINQ to do the same
Access them singularly via "Controls.Find("ntextBoxX", false)", where X is the number of course.
I'll show the LINQ approach as I like it better.
int sum = Controls.Cast<Control>().Sum(c => c.Name.StartsWith("ntextBox") ? int.Parse(c.Text) : 0);
I haven't tested the code, but it should work. Tell me if there are any problems.
EDIT: Tested and it works. For the sake of completeness, I'll add some event logic to the TextBox-adding loop, to make sure that their input is actually numeric.
tbox.TextChanged += HandleNTextBoxInput; // add this line
And elsewhere, add this method:
void HandleNTextBoxInput(object sender, EventArgs e)
{
string text = ((TextBox)sender).Text;
if (!Regex.IsMatch(text, "^[1-9][0-9]*$")) //Text is NOT numeric. Remove [1-9] if you want to keep leading zeros.
{
//Set the Text to 0, for example. Or show a message box. Or whatever.
((TextBox)sender).Text = "0";
}
}
As I mentioned in a comment- this code seems to be maybe too advanced for me.
I have no problem with adding the controls, bu still there is a problem how to get the sum from a button click to a textbox.
I probably made some simple mistakes, or something is missing but I really don't know how to fix this problem.
My code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace testprogram
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) // I'm guessing something is missing over here
{
int sum = Controls.Cast<Control>().Sum(c => c.Name.StartsWith("ntextBox") ? int.Parse(c.Text) : 0);
}
void HandleNTextBoxInput(object sender, EventArgs e)
{
string text = ((TextBox)sender).Text;
if (!Regex.IsMatch(text, "^[1-9][0-9]*$")) //Text is NOT numeric. Remove [1-9] if you want to keep leading zeros.
{
//Set the Text to 0, for example. Or show a message box. Or whatever.
((TextBox)sender).Text = "0";
}
}
private int _oldNUDvalue = 0; //or assign it to the default value
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
{
int delta = (int)numericUpDown1.Value - _oldNUDvalue;
if (delta < 0)
{
for (int i = -delta; i > 0; i--)
{
var tbox = Controls.Find("ntextBox" + (_oldNUDvalue - i), false)[0];
Controls.Remove(tbox);
}
}
else if (delta > 0)
{
for (int i = _oldNUDvalue; i < _oldNUDvalue + delta; i++)
{
var tbox = new TextBox();
tbox.Location = new Point(15, 55 + (30 * i));
tbox.Name = "ntextBox" + i;
tbox.TextChanged += HandleNTextBoxInput;
Controls.Add(tbox);
}
}
_oldNUDvalue = (int)numericUpDown1.Value;
}
}
}
}

Categories

Resources