How do I make a 2D list of TextBoxes? - c#

private List<TextBox>[,] textBoxesList = new List<TextBox>[6,3]; // would be my list soon
private TextBox[,] textBoxes = new TextBox[6,3]; // my array of text boxes
public Form1()
{
InitializeComponent();
for (int j = 0; j < textBoxes.Length(0); j++)
{
for (int i = 0; i < textBoxesList.GetLength(1); i++)
{
textBoxes[j,i] = new TextBox();
textBoxes[j,i].Size = new Size(35, 20);
textBoxes[j,i].Font = new Font("Microsoft Sans Serif", 8, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
textBoxes[i].Location = new System.Drawing.Point(90 + j * 50, 50 + i * 30);
textBoxes[j,i].Parent = this;
this.Controls.Add(textBoxes[j,i]);
}
}
}
So this is how I've done it with an array. Now I'm very inexperienced at lists. In fact I never learned lists before but have briefly heard that they can grow/shrink compared to arrays. It would be useful for me to use lists so the user can add a row later on.
Tested this code but it's showing blank.
public partial class Form1 : Form
{
//List<List<TextBox>> li = new List<List<TextBox>>();
List<TextBox> litxt = new List<TextBox>();
public Form1()
{
InitializeComponent();
TextBox txt = new TextBox();
txt.Size = new Size(35, 20);
litxt.Add(txt);
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 3; i++)
{
TextBox txt = new TextBox();
litxt.Add(txt);
}
}
}

For 2D list do it like this
List<List<TextBox>> li = new List<List<TextBox>>();
As you need to make a table of text box use a FlowLayoutPanel of fixed width equal to 3 textboxes
Make a list
List<TextBox> litxt = new List<TextBox>();
Add 3 Text box to litxt
for(int i = 0;i<3;i++)
{
TextBox txt = new TextBox();
txt.Size = new Size(35,20);
litxt.Add(txt);
}
Put this Code on the condition where you want to add 3 TextBox
foreach(TextBox txt in litxt)
{
FlowLayoutPanel.Controls.Add(txt);
}
Put This Code
Add a flowlayout panel to your form and perform this code
public Rough()
{
InitializeComponent();
}
private void Rough_Load(object sender, EventArgs e)
{
}
static int i = 0;
private void button1_Click(object sender, EventArgs e)
{
for(int j = 0; j < 3 ; i++,j++)
{
TextBox txt = new TextBox();
txt.Size = new Size(35, 20);
txt.Name = i.ToString();
flowLayoutPanel1.Controls.Add(txt);
}
}

You can use generic Dictionary and make your own key struct to access the elements.
The key struct MyKey:
public struct MyKey
{
private ushort row;
private ushort col;
public int Row { get { return row; } }
public int Col { get { return col; } }
public MyKey(int row, int col)
{
// check if keys are in range between 0 and ushort.MaxValue
if(row < 0 || row > ushort.MaxValue || col < 0 || col > ushort.MaxValue)
throw new ArgumentOutOfRangeException(string.Format("Arguments row and col cannot be less than 0 or greater than {0}.", ushort.MaxValue));
this.row = (ushort) row;
this.col = (ushort) col;
}
// Overriden GetHashCode() that's used by the Dictionary to search through the keys.
public override int GetHashCode()
{
// we just shift Row by 16 Bits to left and make a bitwise or with Col to generate the Hashcode
return ((int) row << 16) | col;
}
}
And now you can use this key with Dictionary:
var textBoxesList = new Dictionary<MyKey, TextBox>();
var rowCount = 6;
var colCount = 3;
for (int row = 0; row < rowCount; row++)
{
for (int col = 0; col < colCount; col++)
{
var key = new MyKey(row, col);
var textBox = new TextBox();
textBox.Size = new Size(35, 20);
textBox.Font = new Font("Microsoft Sans Serif", 8, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
textBox.Location = new System.Drawing.Point(90 + row * 50, 50 + col * 30);
textBox.Parent = this;
// store the key in the Tag property of the TextBox
// so you can get the row and the column of your textbox with
// var key = (MyKey) textBox.Tag
textBox.Tag = key;
textBoxesList.Add(key, textBox);
this.Controls.Add(textBox);
}
}
If you want to get a TextBox by row 2 and column 4 you can write:
var textbox = textBoxesList[new MyKey(row: 2, col: 4)];
You can also walk though the collection:
foreach (var item in textBoxesList)
{
var row = item.Key.Row;
var col = item.Key.Col;
var textbox = item.Value;
// ...
}
And for example you can search the textBoxesList for TextBoxes in a specific row. Let's say we wanna get all Textboxes in row 2 ordered by the column number:
var textBoxesInRow2 = (from item in textBoxesList
where item.Key.Row == 2
orderby item.Key.Col
select item.Value)
.ToArray();

You need to use
List<List<TextBoxes>>
and
You could use a TextBox[,] for this purpose:
private TextBox[,] textboxes;
public YourClass() {
// Add this after the text boxes have actually been set up...
textboxes = new TextBox[,] {
{textbox00, textbox01, textbox02, ...},
{textbox10, textbox11, textbox12, ...},
,,,
};
}
Then you can access textbox00 as textboxes[0,0], textbox56 as textboxes[5,6], etc.

Assuming each row has the same number of checkboxes (e.g. 3) then you could do it like this:
List<TextBox[3]> textboxes = new List<TextBox[3]>
{
new TextBox[3] { textbox1, textbox2, textbox3 };
new TextBox[3] { textbox4, textbox5, textbox6 };
};
Then when you want to add a new row of checkboxes
textboxes.Add(new TextBox[3] { textbox7, textbox8, textbox9 }; );
Note: The DataGridView control may be a better option than TextBoxes for you.
It gives you that Excel like cell structure and you can easily wire a List to it as the data source.

Related

TableLayoutPanel not showing all data

I have table layout panel. Inside each one I create a Panel to insert two different controls in each cell.
I want to fill TableLayoutPanel with all my data, in this case I have 16 should have 16 different cells. So I try:
private void AddRow(IList<DeliveryBreakdownGetViewModel> rModel)
{
tpnlDeliveryBreakdown.RowCount = rModel.Count / 4; //rModel count = 16
tpnlDeliveryBreakdown.ColumnCount = 4;
var row = 0;
var column = 0;
var fullRow = 1;
for (int i = 0; i < rModel.Count; i++)
{
var panel = new Panel();
Labels.Add(rModel[i].DesignGroupName);
var label = new Label
{
AutoSize = true,
Name = "label" + Labels.Count,
Text = rModel[i].DesignGroupName,
Location = new Point(12, YPos)
};
this.Controls.Add(label);
tpnlDeliveryBreakdown.Controls.Add(panel, column, row);
panel.Controls.Add(label);
DeliveryBreakdownLabelsModel.Add(label);
var numericUpDown = new NumericUpDown
{
Name = "numericUpDown" + Labels.Count,
Maximum = decimal.MaxValue,
Minimum = decimal.MinValue,
Value = Decimal.Round(rModel[i].ContractedAmount, 2),
Location = new Point(12, YPos),
Size = new Size(60, 19),
DecimalPlaces = 2,
Tag = rModel[i].DesignGroupId
};
this.Controls.Add(numericUpDown);
panel.Controls.Add(numericUpDown);
DeliveryBreakdownNumericUpDownModel.Add(numericUpDown);
if (fullRow % 4 == 0)
{
row++;
}
fullRow++;
if(column == 4)
{
column = 0;
}
else
{
column++;
}
}
}
But it only show 2 columns and 7 cells:
Why is not showing all cells and it only show 2 columns instead 4. Regards

Need to display an array to a MessageBox with rows of Assignment Names and columns of Student Names

I need to display a transposed array of student grades on assignments to a MessageBox. I have the transposed array part down, but I need the messagebox to have row and column labels.
Right now the output MessageBox only shows a 3 rows by 5 columns matrix of grades:
decimal[,] decGrades = { { 87m, 88m, 89m },
{ 90m, 95m, 100m },
{ 70m, 80m, 90m },
{ 50m, 50m, 50m },
{ 80m, 92m, 94m } };
string[] strStudentNames = { "Adams", "Benson", "Carson",
"Daley", "Edwards" };
string[] strAssignmentNames = { "HW1", "HW2", "Exam1" };
private decimal[,] TransposeMatrix(decimal[,] newArray)
{
decimal[,] TransposedArray = new decimal[newArray.GetLength(1),
newArray.GetLength(0)];
for (int column = 0; column < newArray.GetLength(1); column++)
{
for (int row = 0; row < newArray.GetLength(0); row++)
{
TransposedArray[column, row] = newArray[row, column];
}
}
return TransposedArray;
}
private void BtnTranspose_Click(object sender, EventArgs e)
{
decGrades = TransposeMatrix(decGrades);
////CODE TO SEND GRADES ARRAY TO MESSAGE BOX
string gradesString = "";
for (int i = 0; i < decGrades.GetLength(0); i++)
{
for (int j = 0; j < decGrades.GetLength(1); j++)
{
gradesString += decGrades[i, j] + " ";
}
gradesString += "\n";
}
MessageBox.Show(gradesString, "Transposed Array");
I can't seem to figure out how to display the string array values in strStudentNames as column labels and strAssignmentNames as the row labels.
I really appreciate any help I could get on this. Thanks!
You can try this:
private void BtnTranspose_Click(object sender, EventArgs e)
{
var matrix = TransposeMatrix(decGrades);
// Create a form
var form = new Form();
form.Text = "Transposed Array";
form.Size = new Size(500, 400);
form.StartPosition = FormStartPosition.CenterParent;
// Create a list view
var listview = new ListView();
listview.Dock = DockStyle.Fill;
listview.View = View.Details;
listview.FullRowSelect = true;
// Initialize columns titles with first empty
listview.Columns.Add("");
for ( int indexD2 = 0; indexD2 < matrix.GetLength(1); indexD2++ )
listview.Columns.Add(strStudentNames[indexD2]);
// Add rows with first column as title
for ( int indexD1 = 0; indexD1 < matrix.GetLength(0); indexD1++ )
{
var item = new ListViewItem();
item.Text = strAssignmentNames[indexD1];
for ( int indexD2 = 0; indexD2 < matrix.GetLength(1); indexD2++ )
item.SubItems.Add(matrix[indexD1, indexD2].ToString());
listview.Items.Add(item);
}
// Add the list view to the form and show it
form.Controls.Add(listview);
form.ShowDialog();
}
Instead of creating an on-the-fly form, you can create a custom form by design with a close button at bottom to have a true message box. You will add a method that takes the matrix as parameter and put the code in this method:
public partial MyMessageBox : Form
{
static public Run(decimal[,] matrix, string[] titleColumns, string[] titleRows)
{
if ( titleColumns.Length != matrix.GetLength(1)
|| titleRows.Length != matrix.GetLength(0) )
throw new ArgumentException("Bounds mismatch");
var form = new MyMessageBox();
form.ListView.Columns.Add("");
for ( int indexD2 = 0; indexD2 < matrix.GetLength(1); indexD2++ )
form.ListView.Columns.Add(titleColumns[indexD2]);
for ( int indexD1 = 0; indexD1 < matrix.GetLength(0); indexD1++ )
{
var item = new ListViewItem();
item.Text = titleRows[indexD1];
for ( int indexD2 = 0; indexD2 < matrix.GetLength(1); indexD2++ )
item.SubItems.Add(matrix[indexD1, indexD2].ToString());
form.ListView.Items.Add(item);
}
form.ShowDialog();
}
}
So you will use it like that:
private void BtnTranspose_Click(object sender, EventArgs e)
{
MyMessageBox.Run(TransposeMatrix(decGrades), strStudentNames, strAssignmentNames);
}

C# Windows Form: Associating a text box each with selected ListBox Item

I created a CheckedListBox of about 12 Items. I want to dynamically create Textboxes to display and accept an input value from the user on the form each time an Item is selected. The Textboxes value is to be associated with the Selected Items Values for storage into some Sql database table when a Button is finally selected.
What I have been able to do so far is to have a single Textbox displayed even when I selected other items from the list box. Below is my Code
private void checkedList_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (e.NewValue == CheckState.Checked)
{
int n = 6;
selectedList.Items.Add(checkedList.SelectedItem.ToString());
for (int x=0; x < checkedList.SelectedItems.Count; x++)
{
TextBox[] txtBoxes = new TextBox[n];
for (int i = 0; i < n; i++)
{
txtBoxes[i] = new TextBox();
}
for (int i = 0; i < n; i++)
{
this.Controls.Add(txtBoxes[i]);
txtBoxes[i].Location = new Point(450, 100);
txtBoxes[i].Size = new System.Drawing.Size(25, 20);
txtBoxes[i].Validating += new CancelEventHandler(txt_Validating);
}
}
}
else
{
selectedList.Items.Remove(checkedList.SelectedItem.ToString());
}
}
private void InitializeMyListBox()
{
for (int x = 0; x < selectedList.Items.Count; x++)
{
selectedList.SetSelected(x, true);
}
// Force the ListBox to scroll back to the top of the list.
selectedList.TopIndex = 0;
}
What did I do wrong?
Can you please help with this.
You have set same location to all TextBoxes.
Try to set something like this:
txtBoxes[i].Location = new Point(450 + i*20, 100);

Displaying HeatMap in DataGridView from List<List<T>> in C#

I've got some data in List of List of touples. My task is to make a heatmap from that.
Being newbie in C#, I've searched the net and found a way of solving this task by painting elements in DataGridView, but I don't understand, how to do it.
So,I've got a list of Touples:
using SpikeDataPacket = List<Tuple<double, double>>;
This is the way I load data inside the grid:
public HeatForm(List<SpikeDataPacket> list)
{
SpikeList = list;
InitializeComponent();
var bindstim = new BindingList<SpikeDataPacket>(SpikeList);
var stimsource = new BindingSource(bindstim, null);
heatMap.DataSource = stimsource;
}
But this displays a table with "capacity" and "count" inside the DataGridView, but not the data.
Also, I've found the way to count the color, but don't know, how to apply it:
private Color HeatMapColor(double value, double min, double max)
{
Color firstColour = Color.RoyalBlue;
Color secondColour = Color.LightSkyBlue;
// Example: Take the RGB
//135-206-250 // Light Sky Blue
// 65-105-225 // Royal Blue
// 70-101-25 // Delta
int rOffset = Math.Max(firstColour.R, secondColour.R);
int gOffset = Math.Max(firstColour.G, secondColour.G);
int bOffset = Math.Max(firstColour.B, secondColour.B);
int deltaR = Math.Abs(firstColour.R - secondColour.R);
int deltaG = Math.Abs(firstColour.G - secondColour.G);
int deltaB = Math.Abs(firstColour.B - secondColour.B);
double val = (value - min) / (max - min);
int r = rOffset - Convert.ToByte(deltaR * (1 - val));
int g = gOffset - Convert.ToByte(deltaG * (1 - val));
int b = bOffset - Convert.ToByte(deltaB * (1 - val));
return Color.FromArgb(255, r, g, b);
}
Thank you in advance!
I think I would tackle the problem somewhat differently.
I would start without using DataBinding. Neither the List of List of Tuple structure nor the mapping of a double to a Color lends itself to well to DataBinding.
I'm also not sure about your color mapping algorithm..
To fill the data into a DataGridView DGV I use a simple routine, which first prepares the DGV and then paints the Cells:
void fillData()
{
int maxRow = data.Count;
int maxCol = data[0].Count;
double factor = 1.0;
DGV.RowHeadersVisible = false;
DGV.ColumnHeadersVisible = false;
DGV.AllowUserToAddRows = false;
DGV.AllowUserToOrderColumns = false;
DGV.CellBorderStyle = DataGridViewCellBorderStyle.None;
//..
int rowHeight = DGV.ClientSize.Height / maxRow - 1;
int colWidth = DGV.ClientSize.Width / maxCol - 1;
for (int c = 0; c < maxRow; c++) DGV.Columns.Add(c.ToString(), "");
for (int c = 0; c < maxRow; c++) DGV.Columns[c].Width = colWidth;
DGV.Rows.Add(maxRow);
for (int r = 0; r < maxRow; r++) DGV.Rows[r].Height = rowHeight;
List<Color> baseColors = new List<Color>(); // create a color list
baseColors.Add(Color.RoyalBlue);
baseColors.Add(Color.LightSkyBlue);
baseColors.Add(Color.LightGreen);
baseColors.Add(Color.Yellow);
baseColors.Add(Color.Orange);
baseColors.Add(Color.Red);
List<Color> colors = interpolateColors(baseColors, 1000);
for (int r = 0; r < maxRow; r++)
{
for (int c = 0; c < maxRow; c++)
{
DGV[r,c].Style.BackColor =
colors[ Convert.ToInt16( data[r][c].Item2 * factor)];
}
}
}
You would wnat to change a few things, especially the base colors and the number of colors you want to get, depending on your values but also the mapping from a double value to a integer index!
Here is the function to create a list of interpolated colors. It takes a few base colors and a length N and returns N interpolated colors. This makes the mapping simple and flexible..
List<Color> interpolateColors(List<Color> stopColors, int count)
{
SortedDictionary<float, Color> gradient = new SortedDictionary<float, Color>();
for (int i = 0; i < stopColors.Count; i++)
gradient.Add(1f * i / (stopColors.Count-1), stopColors[i]);
List<Color> ColorList = new List<Color>();
using (Bitmap bmp = new Bitmap(count, 1))
using (Graphics G = Graphics.FromImage(bmp))
{
Rectangle bmpCRect = new Rectangle(Point.Empty, bmp.Size);
LinearGradientBrush br = new LinearGradientBrush
(bmpCRect, Color.Empty, Color.Empty, 0, false);
ColorBlend cb = new ColorBlend();
cb.Positions = new float[gradient.Count];
for (int i = 0; i < gradient.Count; i++)
cb.Positions[i] = gradient.ElementAt(i).Key;
cb.Colors = gradient.Values.ToArray();
br.InterpolationColors = cb;
G.FillRectangle(br, bmpCRect);
for (int i = 0; i < count; i++) ColorList.Add(bmp.GetPixel(i, 0));
br.Dispose();
}
return ColorList;
}
My test data were created like this:
List<List<Tuple<double,double>>> data = new List<List<Tuple<double,double>>>();
Random R = new Random();
void createData(int maxRow, int maxCol)
{
for (int c = 0; c < maxRow; c++)
{
data.Add(new List<Tuple<double, double>>());
for (int r = 0; r < maxRow; r++)
{
data[c].Add(new Tuple<double, double>(c, Math.Min(999, R.Next(r*c))));
}
}
}
and I used it like this:
private void Form1_Load(object sender, EventArgs e)
{
createData(40, 40);
fillData();
}
Here is a (rather boring) screenshot:
Once you got the display as you want it, you may want to decide about going for a solution with DataBinding. I believe that you will need to use OwnerDrawing the Cells. Once you have the values in the cells you can use the same mapping simply like this:
private void DGV_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
Color theColor = .....
e.Graphics.Clear(colors[theColor]);
}
Here is a simple way to color items:
Our object of reference:
internal class Item
{
public string Name { get; set; }
public double Value { get; set; }
}
Usage:
paste the code and re-build your project
go to Data Sources pane and create a new data source
select that type of object and press Finish
drop the created source to your from (it creates a DataGridView)
Coloring the cells:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// create some items
itemBindingSource.Add(new Item {Name = "name1", Value = 0});
itemBindingSource.Add(new Item {Name = "name2", Value = 0.5});
itemBindingSource.Add(new Item {Name = "name3", Value = 1});
// find column index to color
string columnName = "Value";
int columnIndex = -1;
DataGridViewColumnCollection columns = itemDataGridView.Columns;
for (int i = 0; i < columns.Count; i++)
{
DataGridViewColumn column = columns[i];
if (column.DataPropertyName == columnName)
{
columnIndex = i;
break;
}
}
// color cells
if (columnIndex >= 0)
{
foreach (DataGridViewRow row in itemDataGridView.Rows)
{
if (!row.IsNewRow)
{
// get associated data
var item = (Item) row.DataBoundItem;
// build color from associated data
Color fromArgb = Color.FromArgb((int) (item.Value*255), 128, 128);
row.Cells[columnIndex].Style.BackColor = fromArgb;
}
}
}
}
}
Roll up your own coloring logic then,

Creating and positioning an array of buttons

Hello everyone I need some help with the positioning of an array of buttons.I want to make this function so it scans the name of the previous button and it names the next one +1,afterwards I want to position these buttons on the screen having a certain space between them and them being positioned in the center of the screen.I have tried many times to modify my method but I don't know how to get this method to work.
This is how my method looks like.
UPDATED
PS.Reference not set to an instance of an object Q.Q
public Button[] ButtonCreator(byte numOfBtnsNeeded,Form1 form)
{
Button[] mybtns = new Button[numOfBtnsNeeded];
foreach (Button b in mybtns)
{
for (int i = 0; i < mybtns.Length; i++)
{
mybtns[i].Name = i.ToString();
mybtns[i].Parent = form;
mybtns[i].Height = 50;
mybtns[i].Width = 50;
for (int k = i + 1; k < mybtns.Length; k++)
{
mybtns[i].Location = new Point(190, 80);
mybtns[k].Location = Point.Add(new Point(mybtns[i].Location.X + 10,mybtns[i].Location.Y + 10),new Size(mybtns[i].Size.Width,mybtns[i].Size.Height));
}
}
}
foreach (Button b in mybtns)
{
b.Show();
}
return mybtns;
}
Play with this example...
public partial class Form1 : Form
{
private List<List<Button>> grid = new List<List<Button>>();
public Form1()
{
InitializeComponent();
byte numRows = 5;
byte numCols = 5;
for (byte i = 0; i < numRows; i++)
{
grid.Add(ButtonRowCreator(numCols, 25, (i+1) * 50));
}
}
public List<Button> ButtonRowCreator(byte numOfBtnsNeeded, int x, int y)
{
List<Button> btns = new List<Button>();
for (int i = 0; i < numOfBtnsNeeded; i++)
{
Button btn = new Button();
btn.Size = new Size(50, 50);
btn.Location = new Point(x + (i * btn.Width), y);
btns.Add(btn);
this.Controls.Add(btn);
btn.Click += new EventHandler(btn_Click);
}
return btns;
}
void btn_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
btn.Text = "X";
int curRow = -1, curCol = -1;
for(int i = 0; i < grid.Count; i++)
{
int index = grid[i].IndexOf(btn);
if (index != -1)
{
curRow = i;
curCol = index;
Console.WriteLine("curRow = " + curRow.ToString() + ", curCol = " + curCol.ToString());
}
}
// ... now you can use "curRow", "curCol" and "grid" to do something ...
// reset all BackColors:
foreach (List<Button> row in grid)
{
foreach (Button col in row)
{
col.BackColor = Button.DefaultBackColor;
}
}
// the below should give you some examples for the
// syntax necessary to access buttons in the grid
// highlight current row:
foreach (Button col in grid[curRow])
{
col.BackColor = Color.Yellow;
}
// highlight current col:
for (int i = 0; i < grid.Count; i++)
{
grid[i][curCol].BackColor = Color.Yellow;
}
}
}
You cannot change a foreach variable reference (ie b). If you want to initialize an array you should use for loop:
for(int i = 0; i < numOfBtnsNeeded; i++)
{
var button = mybtns[i] = new Button();
//Here you can modify the reference of button.
}
Also, mybtns will be full of nulls since Button is a reference type which means it's default value is a null.
you want something like:
public Button[] ButtonCreator(byte numOfBtnsNeeded)
{
Button[] mybtns = new Button[numOfBtnsNeeded];
for (int i = 0; i < mybtns.Length; i++)
{
mybtns[i] = new Button();
mybtns[i].Name = (i + 1).ToString();
}
return mybtns;
}
I'm not sure why you're using a byte over an int, but it works either way.
Essentially, when you create the array, you're not creating the objects within the array. And you cannot modify the thing you are looping over within a foreach loop, so you need a for loop.

Categories

Resources