Grabbing input from dynamically created textboxes to another form - c#

So, we have a way to dynamically create an array of x by y of texboxes to input values into it to later on do some matrix calculation, like determine the rank of the matrix.
To generate the array we use this:
protected void Form2_Load(object sender, EventArgs e)
{
for (int row = 0; row < LinhaText; row++)
{
List<TextBox> newLin = new List<TextBox>();
textboxes.Add(newLin);
for (int col = 0; col < ColunText; col++)
{
TextBox newbox = new TextBox();
newbox.Width = textboxWidth;
newbox.Height = textboxHeight;
newbox.Top = (row * (textboxHeight + spacing)) + spacing + vOffset;
newbox.Left = (col * (textboxWidth + spacing)) + spacing + hOffset;
newLin.Add(newbox);
this.Controls.Add(newbox);
}
}
}
This stands on the second form, as in the first one we input the dimensions of the array. When we input the values on the textboxes they should get sent to the third form to get used by the other calculations.
We are wondering how we grab the values and send them to the other form to later on display the results.
Any further information you need, just ask!

I'm assuming that these are not modal windows and that the user can switch between them at any point? If that is the case then I would suggest using a design pattern like MVC or MVVM, separating your data, view, and control logic. By keeping the data layer distinct from the view, you can have all of your windows (views) share the same underlying data. Of course, you'll want to use events to notify the various windows of when the data is changed (see the INotifyPropertyChanged Interface).

I would not pass around textboxes, instead store the numbers in a 2-dimensional array (matrix). Also I would use binding to automatically bind the matrix values to the textboxes. To make binding work, you need a helper class containing your values, because binding needs to bind to a property.
private class Data // Can be a nested private class in Form2.
{
public double Value { get; set; }
public override string ToString() => Value.ToString(); // Not strictly necessary, but
// makes debugging easier.
}
We create a _matrix field as well as a property converting this Data[,] matrix to a double[,] matrix and returning it (in Form2):
private Data[,] _matrix;
public double[,] Matrix
{
get {
var m = new double[LinhaText, ColunText];
for (int row = 0; row < LinhaText; row++) {
for (int col = 0; col < ColunText; col++) {
m[row, col] = _matrix[row, col].Value;
}
}
return m;
}
}
Now create the textboxes and bind them to a Data object (In Form2_Load or in the form constructor after InitializeComponent):
_matrix = new Data[LinhaText, ColunText];
for (int row = 0; row < LinhaText; row++) {
for (int col = 0; col < ColunText; col++) {
var newbox = new TextBox {
Width = textboxWidth,
Height = textboxHeight,
Top = (row * (textboxHeight + spacing)) + spacing + vOffset,
Left = (col * (textboxWidth + spacing)) + spacing + hOffset
};
// Create Data object, add it to the matrix and bind it to the TextBox.
var data = new Data();
_matrix[row, col] = data;
newbox.DataBindings.Add(new Binding("Text", data, "Value", true));
Controls.Add(newbox);
}
}
Note that binding works two way. If you initialize the matrix with numbers, they will be displayed in the textboxes when opening the form. Numbers entered in textboxes will be stored in the matrix.
Now, on the other form, you need a reference to this form. Then you can get the matrix with
double[,] matrix = frmMartixInput.Matrix;
If you have difficulties passing around form references, then use a static property in a static class for the matrix, that can be referenced by all the forms.
public static class Globals
{
public static double[,] Matrix { get; set; }
}
You can also pass data to a form through constructor parameters (either a reference to another form or the matrix itself).

Related

How to get a 2D array of strings across the photon network in a Unity multi player game

I am trying to serialize a 2D array of strings to send over a network.
I have a couple ideas but I seem to be blocked by api or efficiency. I really don't want to serialize and send a small 2D array across a network 10 times a second when I only need it when I collide with something and ask for the information.
I have a list of items a player has in his cart. I want to make it okay to steal from that cart, so the other player has to be able to see what's in the cart.
If I use onphotonserializeview I thought I could convert the flatten the 2d array of strings into a 1D array of strings with a delimiter and stream.sendnext the delimited line and reconstitute on the other side, or some variation of that functionality.
public void serialize_merchandise_list() {
int width = 0;
int length = 0;
while((width < cart_list_width) && (length < cart_list_width)) {
width++;
if (width >= cart_list_width) {
length++;
width = 0;
}
// convert to bytes and then to string and append
System.Text.ASCIIEncoding.ASCII.GetBytes(merchandise_items[width, length]));
}
}
But doing that unnecessarily is an operation time nightmare, I want it on demand.
I was going to do a punrpc but I can't get a return value or ref parameter. What is the best approach here?
I want:
collide with cart
do you want to steal from cart --> yes --> show list
else --> go on your way
Afaik a one-dimensional array of strings would be no problem for Photon.
So I guess you could just flatten the array and additionally send the second size - lets name it element size (in contrary to the first one - the element count) as parameters.
Like e.g. lets say your array looks like
string[,] array2D = new string[4,2]{{"a", "b"}, {"c","d"}, {"e","f"}, {"g","h"}};
Then you would know the second (elementSize) dimension using either hardcoded or GetLength(int)
var elementSize = array2D.GetLength(1);
and you would simply flatten the array using
string[] arrayFlat = array2D.Cast<string>().ToArray();
So you would send these two informations via photon (I don't know photon in detail) but afaik you can do this one time without having to send it continiously. The two parameters would be
int elementSize, string[] arrayFlat
in this case with the values
2, string[8]{"a","b","c","d","e","f","g","h"}
So together:
public void SendArray2D()
{
int elementSize = array2D.GetLength(1);
string[] arrayFlat = array2D.Cast<string>().ToArray();
photonView.RPC(nameof(ReceiveArray2D), RpcTarget.All, elementSize, (object) arrayFlat);
}
Then on the receiver part you are getting
int elementSize = 2, string[] arrayFlat = string[8]{"a","b","c","d","e","f","g","h"}
and have to convert it back. You already know the second dimension elementSize = 2 so in order to get also the first one you simply do
var elementCount = arrayFlat.Length / elementSize; // = 4
so you know the 2D array you will have to fill would be
string[,] array2D = new string[elementCount, elementSize]; // = 4,2
and then you can simply iterate it and do something like
for (var x = 0; x < elementCount; x++)
{
for (int y = 0; y < elementSize; y++)
{
array2D[x, y] = arrayFlat[x * elementSize + y];
}
}
So together
[PunRpc]
public void ReceiveArray2D(int elementSize, string[] arrayFlat)
{
var elementCount = arrayFlat.Length / elementSize;
array2D = new string[elementCount, elementSize];
for (var x = 0; x < elementCount; x++)
{
for (int y = 0; y < elementSize; y++)
{
array2D[x, y] = arrayFlat[x * elementSize + y];
}
}
// Could also e.g. call some event like
OnReceivedArray2D?.Invoke (array2D);
}
public event Action<string[,]> OnReceivedArray2D;
So you could attach listeners to the event via
reference.OnReceivedArray2D += SomeHandler;
private void SomeHandler(string [,])
{
// ...
}
Or alternatively you could implement a class that stores your data in a flat array but lets you access it like if it would be a 2D array (in general a 2D array is anyway stored in memory as a flat one)
[Serializable]
public class SerializableArray2D
{
public readonly string[] ArrayFlat;
public readonly ElementSize;
public SerializableArray2D(int elementSize, string[] arrayFlat)
{
ElementSize = elementSize;
ArrayFlat = arrayFlat;
}
public SerializableArray2D(string[,] array2D) : this(array2D.GetLength(1), array2D.Cast<string>().ToArray()) { }
public SerializableArray2D(int elementSize, int elementCount) : this(elementSize, new string[elementCount]){}
public string this[int x, int y]
{
get { return ArrayFlat[x * ElementSize + y]; }
set { ArrayFlat[x * ElementSize + y] = value; }
}
}
usage would be e.g. Initialize it
var array2D = new string[4,2]{{"a", "b"}, {"c","d"}, {"e","f"}, {"g","h"}};
var serializableArray2D = new SerializableArray2D(array2D);
For accessing specific indices like in a 2D array
string example = serializableArray2D[1,1];
serializableArray2D[3,0] = "example";
For sending it
photonView.RPC(nameof(Receive), RpcTarget.All, serializableArray2D.ElementSize, (object)serializableArray2D.ArrayFlat);
And when receiving it
[PunRPC]
public void Receive(int elementSize, string[] arrayFlat)
{
serializableArray2D = new SerializableArray2D(elementSize, arrayFlat);
// Again e.g. call an event
OnReceivedSerializableArray2D?.Invoke(serializableArray2D);
}
public event Action<SerializableArray2D> OnReceivedSerializableArray2D;

How to prevent overriding array entries in a for-loop?

I got the hint for my program to make it easier to read and maintenance with using class structures instead of a mass of arrays and matrices. (I didn't find similar topics here, but maybe I used the wrong vocabulary. But although I hope to get help)
As I have a dataset with multiple entries I don't wanna have all of them but some specific ones. So I build a class to store them easier:
public class ControlPoint
{
// classify necessary variables
public double _collimatorangle;
public double _gantryangle;
public double[] _jawpositions = new double[4];
public double _monitorunits;
public double[] _mlcs = new double[120];
public double _beamenergy;
// construct the class
public ControlPoint()
{
}
}
Calling and filling them is not the problem but I have over 100 of those ControlPoint objects, so I wanted to use a for loop to fill them and store them in a new array/list/ArrayList. In the end, I would like to use the data for calculating differences but converting this class into double doesn't work. So I wrote this
ControlPoint[] DataPoints = new ControlPoint[160];
ControlPoint CoPos = new ControlPoint();
for (int j = 0; j < 160; j++)
{
// reading data from file
// ...
CoPos._jawpositions[0] = Convert.ToDouble(ReadData_1);
CoPos._jawpositions[1] = Convert.ToDouble(ReadData_2);
CoPos._jawpositions[2] = Convert.ToDouble(ReadData_3);
CoPos._jawpositions[3] = Convert.ToDouble(ReadData_4);
DataPoints[j] = CoPos;
}
So after the loop, I expected a DataPoints array with different values for each array entry. But in Debugging I saw that changing the data e.g. at j = 10 all values in DataPoints from 0 to 9 change to the actual value.
I don't know where I did a wrong step and hope you can help me to prevent that overriding.
You're only creating one instance of ControlPoint and modifying it over and over, then assigning it to every element of the array. Move the instantiation inside the loop:
for (int j = 0; j < 160; j++)
{
ControlPoint CoPos = new ControlPoint();
// reading data from file
// ...
CoPos._jawpositions[0] = Convert.ToDouble(ReadData_1);
CoPos._jawpositions[1] = Convert.ToDouble(ReadData_2);
CoPos._jawpositions[2] = Convert.ToDouble(ReadData_3);
CoPos._jawpositions[3] = Convert.ToDouble(ReadData_4);
DataPoints[j] = CoPos;
}
You are assigning the same CoPos object to every element in your DataPoints array, only altering the value on every loop. Create a new object inside the loop:
ControlPoint[] DataPoints = new ControlPoint[160];
for (int j = 0; j < 160; j++)
{
// reading data from file
// ...
var CoPos = new ControlPoint();
CoPos._jawpositions[0] = Convert.ToDouble(ReadData_1);
CoPos._jawpositions[1] = Convert.ToDouble(ReadData_2);
CoPos._jawpositions[2] = Convert.ToDouble(ReadData_3);
CoPos._jawpositions[3] = Convert.ToDouble(ReadData_4);
DataPoints[j] = CoPos;
}

C# - Listbox not displaying items

I'm working on a program that functions as a carpark simulator. I want the user interface to display how many spaces are available on each level at any given time. I've attempted to do this using listboxes (one for level number, one for number of spaces on that level), but can't get it to work. For some reason, the listbox that displays the spaces available (listboxSA) always comes up blank and I have no idea why.
The code that creates the listboxes is shown below:
public void updateLevelLabels(Simulator simulator)
{
//constant integers used for label positioning
//y coordinate for first label
const int YSTARTPOINT = 12;
//x coordinate for all labels
const int XSTARTPOINT = 104;
//create new listbox to show level IDs
ListBox listboxLevels = new ListBox();
//position listbox on form
//constant x-coordinate
listboxLevels.Left = XSTARTPOINT;
//constant y-coordinate
listboxLevels.Top = YSTARTPOINT;
//auto-assumes size depending on content
listboxLevels.AutoSize = true;
//create new listbox to show spaces available
ListBox listboxSA = new ListBox();
//set x and y coordinates
//constant x coordinate
listboxSA.Left = XSTARTPOINT + 38;
//constant y coordinate
listboxSA.Top = YSTARTPOINT;
//auto-resizes depending on content
listboxSA.AutoSize = true;
//populate listboxes
for (int i = 0; i < Length(); i++)
{
//identify level at current index
Level lev = At(i);
//add level unique ID to list
listboxLevels.Items.Add(lev.getLevelID());
//add number of spaces (available) on level to list
listboxSA.Items.Add(lev.getNumSpaces().ToString());
}
//place listboxes on form
simulator.Controls.Add(listboxLevels);
simulator.Controls.Add(listboxSA);
}
I've debugged the code and the value for the lev.numSpaces variable is what I'd expect it to be. I've also tried to select the indices of the listboxSA after it's creation and the created indices are selectable (listbox item becomes highlighted), but there is still no text in them.
I honestly have no idea what could be causing this to happen, especially weird considering the same procedure is essentially carried out on both listboxes with a differe get() function.
If anyone can spot what might be throwing it off, I'd really appreciate any advice!
Code of called functions called shown below:
//from `Levels` class
//Levels acts as a public interface for a `List<Level>`
public int Length()
{
//return number of `Level` instances in collection (int)
return levelList.Count;
}
//from `Level` class
//obtain unique identifer of level
public string getLevelID()
{
//return unique Level name
return levelID;
}
//from `Level` class
//obtain number of spaces on level
//all spaces assumed to be free
public int getNumSpaces()
{
//should = value of Levels.Length()
return numSpaces;
}
Thanks in advance,
Mark
You should better check data which you are trying to apply to listbox or seek problem in other place. I made test which is doing the same as your code and there is no problem.
string[] stringArray = new string[] { "one", "two", "three", "four" };
int[] intArray = new int[] { 1, 2, 3, 4 };
private void Addlistboxes()
{
ListBox lb1 = new ListBox();
ListBox lb2 = new ListBox();
lb1.Left = 10;
lb1.Top = 60;
lb1.AutoSize = true;
lb2.Left = 15 + lb1.Width;
lb2.Top = 60;
lb2.AutoSize = true;
for (int i = 0; i < 4; i++)
{
lb1.Items.Add(stringArray[i]);
lb2.Items.Add(intArray[i]);
}
this.Controls.Add(lb1);
this.Controls.Add(lb2);
}`

Modifying Variable Names in C#

I'm not sure if the title is correct because I wasn't sure how to explain it. I've encountered many scenarios where being able to dynamically modify part of a variable name such a suffixing integer could save me a great deal of time and keep my code much cleaner but I'm not sure how to do it. Here's an example of my most recent encounter.
I have 9 PictureBox's in a 3 x 3 grid. Each PictureBox has a name of cell followed by it's number so cell1, cell2, cell3 etc. I want to get the background colour of each of these cells and assign them to a variable whilst converting them to strings... something like this:
for (int i = 1; i < 10; i++)
{
string ci = celli.BackColor.ToString();
}
Is there a way I can have the i variable insert only it's numeric value to the placeholder rather than appending an i to the variable name? Can I wrap it in some sort of bracket? I've tried Googleing this but I'm finding it difficult to search for using just keywords.
Thanks in advance.
You are probably using a visual form editor, the best way to do this whould probably be to generate the grid by code (and not visually).
Another solution is to make it a matrix:
PictureBox[,] cell = new PictureBox[,] {
{ cell1, cell2, cell3 },
{ cell4, cell5, cell6 },
{ cell7, cell8, cell9 }
};
string[,] c = new string[3, 3];
for(int y=0; y<3; y++)
for(int x=0; x<3; x++)
c[x, y] = cell[x, y].BackColor.ToString();
Good luck with your code.
You would like to generate a list or collection of all your pictureboxes so that you can access them by specifying their index. One way is to generate the PictureBoxes on runtime:
Like this:
List<PictureBox> myPics = new List<PictureBox>();
int picWidth = 100;
int picHeight = 100;
for (x = 0; x <= this.Width; x += picWidth) {
for (y = 0; y <= this.Height; y += picHeight) {
PictureBox pic = new PictureBox();
pic.Image = pic.Image;
// Your image
pic.Location = new Point(x, y);
this.Controls.Add(pic);
myPics.Add(pic);
}
}
// Do something with myPics...
The other method is that when you do have all the pictureboxes on your form already, you can iterate through all the controls, check which ones are pictureboxes and then check their Name property to identify their index. Then do something with them accordingly.
foreach (void ctrl_loopVariable in this.Controls) {
ctrl = ctrl_loopVariable;
if (ctrl.GetType() == typeof(PictureBox)) {
if (ctrl.Name == "your picture box name to test") {
// Do something here with ctrl
}
}
}
(The above code is converted from VB to C#, excuse conversion issues)
Your intend here is to dynamically reference those controls.
In order to achieve this, there is two options:
You create those controls dynamically
You create dynamic references for the controls created by your form-designer
The first point is explained ny Shreyas Kapur's answer.
The second could be cone like this,
readonly Dictionary<Point,PictureBox> _dynamicMappedBoxes =
new Dictionary<Point,PictureBox>();
// Call this once in the beginning ofr your program
void createDynamicMapping()
{
foreach(PictureBox box in Controls.OfType<PictureBox>())
{
Point coords = getCoordinatesFromName(box);
_dynamicMappedBoxes.Add(coords, box);
}
}
Point getCoordinatesFromName(PictrueBox box)
{
int x = int.Parse(box.Name.SubString(IdontKnow);
int y = int.Parse(box.Name.SubString(IdontKnow);
retrun new Point(x,y);
}
//usage
string colorName = dynamicMappedBoxes[new Point(x,y)].BackColor.ToString();

How to pass the whole column of values inside dataGridView into method?

I have a dataGridView containing longitude and latitude value.
I need to make use with the value by passing it into a DoSomethingMethod().
I am not familiar with dataGridView and therefore I copy all the value from dataGridView and handle it wit List<>
Below is my code to manipulate the data inside dataGridView
Public Main()
{
List<double> lst_Long = new List<double>();
List<double> lst_Latt = new List<double>();
for (int rows = 0; rows < dataGridView1.Rows.Count; rows++)
{
lst_Long.Add(Convert.ToDouble(dataGridView1.Rows[rows].Cells["Long"].Value));
lst_Latt.Add(Convert.ToDouble(dataGridView1.Rows[rows].Cells["Latt"].Value));
}
int ColumnCount = lst_Long.Count - 1;
int RowCount = lst_Row.Count - 1;
//Pass the list to DoSomethingMethod
DoSomethingMethod(lst_Long , lst_Latt, ColumnCount, RowCount);
}
DoSomethingMethod(List<double> lst_long, List<double> lst_latt, int ColumnCount, int RowCount)
{
for (int iColumn = 0; iColumn < ColumnCount; iColumn++)
{
for (int iRow = 0; iRow < RowCount; iRow++)
{
// access my latitude value and longitude value here
double myX = lst_latt[iRow]
double myY = lst_long[iRow]
// do some processing here with the value, lets say....
double myNewX = myX + 10;
double myNewY = myY + 10;
PassNewDataToAnotherMethod( myNewX , myNewY );
}
}
}
I would like to ask, if there is any other way to direct use and get the cell value from the dataGridView at Columns["Long"] and Columns["Latt"] and do the similar thing with out copy value from dataGridView into the list?
Thanks~
Update: Below is my DGV table (The data is random, not real)
Update my own answer and this question shall close
// Pass the whole dataGridView1 into method and access the value through row count.
DoSomethingMethod(DataGridView dataGridView1)
{
for (int rows = 0; rows < dataGridView1.Rows.Count; rows++)
{
double myX = Convert.ToDouble(dataGridView1.Rows[rows].Cells["Latt"].Value);
double myY = Convert.ToDouble(dataGridView1.Rows[rows].Cells["Long"].Value);
}
}
One way of doing that (that comes to my mind) is using BindingSource with custom model, so instead of operating on rows you would operate on databound model. Something like that:
public class Location
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
//binding grid
var bs = new BindingSource();
bs.DataSource = listOfLocations; // List<Location>
dataGridView1.DataSource = bs; // thanks to binding source all changes in datagrid are
// propagated to underlying data source (ie. List<Location>
Then you can just operate on listOfLocations without accessing values in cells by yourself.
I am not sure what you are actually trying to achieve. Here is one more solution. Hope this solution is useful for you.
You can create a list of tuples using linq and send it to the NewDoSomething method as shown below
Public Main()
{
//Generate a list of Tuples using linq
var tuples = from row in dataGridView1.Rows.Cast<DataGridViewRow>()
select new Tuple<double, double>(
Convert.ToDouble(row.Cells["longitude"].Value),
Convert.ToDouble(row.Cells["latitude"].Value));
//Pass the tuple list to DoSomethingMethod
DoSomethingMethod(tuples);
}
private void DoSomeThing(IEnumerable<Tuple<double, double>> tuples)
{
//Iterate through the tuples
foreach (Tuple<double, double> tuple in tuples)
{
double myX = tuple.Item1;
double myY = tuple.Item2;
}
}
Of course this code could be refined. This just shows the approach you can use.
If you are using a DataTable as the datasource, you may try this:
public void Main()
{
DoSomethingMethod((DataTable)dataGridView1.DataSource);
}
private void DoSomethingMethod(DataTable dataTable)
{
foreach (DataRow row in dataTable.Rows)
{
double myX = Convert.ToDouble(row["Latt"]);
double myY = Convert.ToDouble(row["Long"]);
}
}

Categories