how to access the control within a control in tablelayout - c#

I have a Class(Conatiner) object in a tablelayoutpanel cell. I want to access that textfield in that specific field. How can I take the values on a button click?
I want to access the 1 2 3 with the Channel and the X and Y values. But I do not know the number objects in the tableLayoutPanel
Here is the code I have written so far
private void masterTab1_SaveButton_Click(object sender, EventArgs e)
{
var colWidths = this.MatrixPanel.GetColumnWidths();
var rowHeights = this.MatrixPanel.GetRowHeights();
int col = -1, row = -1;
int offset = 0;
for (int iRow = 0; iRow < this.MatrixPanel.RowCount; ++iRow)
{
offset += rowHeights[iRow];
row = iRow;
for (int iCol = 0; iCol < this.MatrixPanel.ColumnCount; ++iCol)
{
offset += colWidths[iCol];
col = iCol;
var myCellControl = MatrixPanel.GetControlFromPosition(col, row);
if (myCellControl is Container)
{
Adapter.insertposition(RackID, row, col, //Want the Channel Value , "Ready");
}
}
}
}

if your "Container" class is properly setup/has all the properties or controls you need to get the information you want, then i believe what you are looking for is this:
if (myCellControl is Container)
{
Container tmp = myCellControl as Container;
//after this point, you can reference the controls/properties of your
//Container class/control using tmp... see example below as i do not know
//what your Container control exposes as far as properties are concerned.
Adapter.insertposition(RackID, row, col, tmp.ChannelValue, "Ready");
}

Related

Making a list for each cell in a datagridview

I have a 9x9 DataGridView and each cell can only contain a number between 1-9 (like sudoku). I wish to have some sort of list for each cell containing all 9 of these numbers. As the game goes on i will need to start removing numbers from different cell's "list". could i get some advice on what i should use in this situation? is there a way of avoiding making 81 different lists and manually inserting all 9 numbers into all 81 lists?
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
string listname;
listname = "cell" + i.ToString() + j.ToString();
List<int> listname = new List<int>();
}
}
I was thinking something like this would help with making the 81 different lists as the list names would be cell(rownum)(columnnum) but this obviously doesn't work.
To give you an idea of what I mean, create yourself an enumeration like the following:
[Flags]
public enum SudokuValue
{
None = 0,
One = 1,
Two = 2,
Three = 4,
Four = 8,
Five = 16,
Six = 32,
Seven = 64,
Eight = 128,
Nine = 256
}
The Flags attribute is important, as we shall see later. You can now take advantage of this enumeration in a class looking something like:
public class SudokuHelper
{
private SudokuValue[,] gridValues;
private bool[,] gridFills;
public SudokuHelper()
{
gridValues = new SudokuValue[9, 9];
gridFills = new bool[9, 9];
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
gridValues[i, j] = (SudokuValue)511;
gridFills[i, j] = false;
}
}
}
public void UpdateGrid(int row, int col, int val)
{
gridFills[row, col] = true;
SudokuValue sudoku = (SudokuValue)(Math.Pow(2, val - 1));
gridValues[row, col] = sudoku;
//Update same col
for (int i = 0; i < 9; i++)
{
if (!gridFills[i, col] && gridValues[i, col].HasFlag(sudoku))
{
gridValues[i, col] -= sudoku;
}
}
//Update same col
for (int i = 0; i < 9; i++)
{
if (!gridFills[row, i] && gridValues[row, i].HasFlag(sudoku))
{
gridValues[row, i] -= sudoku;
}
}
//Update same block
int startBlockRow;
int startBlockCol;
if (row < 3)
{
startBlockRow = 0;
}
else if (row < 6)
{
startBlockRow = 3;
}
else
{
startBlockRow = 6;
}
if (col < 3)
{
startBlockCol = 0;
}
else if (col < 6)
{
startBlockCol = 3;
}
else
{
startBlockCol = 6;
}
for (int i = startBlockRow; i < startBlockRow + 3; i++)
{
for (int j = startBlockCol; j < startBlockCol + 3; j++)
{
if (!gridFills[i, j] && gridValues[i,j].HasFlag(sudoku))
{
gridValues[i, j] -= sudoku;
}
}
}
}
public string GetStringRepresentation(int row, int col)
{
var sB = new StringBuilder();
if (gridValues[row, col].HasFlag(SudokuValue.One))
{
sB.Append(",1");
}
if (gridValues[row, col].HasFlag(SudokuValue.Two))
{
sB.Append(",2");
}
if (gridValues[row, col].HasFlag(SudokuValue.Three))
{
sB.Append(",3");
}
if (gridValues[row, col].HasFlag(SudokuValue.Four))
{
sB.Append(",4");
}
if (gridValues[row, col].HasFlag(SudokuValue.Five))
{
sB.Append(",5");
}
if (gridValues[row, col].HasFlag(SudokuValue.Six))
{
sB.Append(",6");
}
if (gridValues[row, col].HasFlag(SudokuValue.Seven))
{
sB.Append(",7");
}
if (gridValues[row, col].HasFlag(SudokuValue.Eight))
{
sB.Append(",8");
}
if (gridValues[row, col].HasFlag(SudokuValue.Nine))
{
sB.Append(",9");
}
//remove leading comma
return sB.ToString().Substring(1);
}
}
The constructor creates an initial two-dimensional array of SudokuValues and populates it with an initial value representing all the possible values 1 ... 9 (511). Note that although 511 is not listed within the enumeration, we can still cast it to a SudokuValue. This is because all enumerations are by default integers. It also creates and populates a two-dimensional array of bools, to represent those cells for which values have been provided.
It then provides a method to update the values in the grid, if a value is entered into one of the cells. This method takes three parameters, the row and column of the updated cell, and the value entered into the cell as an integer (necessarily 1 .. 9). First of all it updates the cell itself in the grid (effectively wiping all the other possibilities), and sets the boolean flag. It then proceeds to remove the entered value as a possibility in the other cells in the same column, the same row, and the same 3x3 block. Here it checks before removing the value, that the value previously still was there (using the HasFlags method - available because we set the Flags attribute above), to avoid the cell being entered twice. A little trick here: the entered value (1 .. 9) can be converted to its enumeration equivalent, by reducing it by 1 and then taking 2 to the resulting power.
Finally the helper provides a textual representation of what any given cell contains (either a single value if set, or the remaining possibilities).
To test this, we can do something like this (I was using a WinForms app to test hence the MessageBox.Show()):
var sH = new SudokuHelper();
for (int i = 0; i < 9; i++)
{
var sB = new StringBuilder();
for (int j = 0; j < 9; j++)
{
sB.Append(" ; " + sH.GetStringRepresentation(i, j));
}
MessageBox.Show($"Row {i}: " + sB.ToString().Substring(3));
}
sH.UpdateGrid(4, 7, 9);
for (int i = 0; i < 9; i++)
{
var sB = new StringBuilder();
for (int j = 0; j < 9; j++)
{
sB.Append(" ; " + sH.GetStringRepresentation(i, j));
}
MessageBox.Show($"Row {i}: " + sB.ToString().Substring(3));
}
If it was me building this app, I would use a 9x9 grid of TextBoxes (rather than a DataGridView), all with a KeyDown/KeyPress event, that calls the SudokuHelper to update the Grid if one of the keys 1 .. 9 is pressed. But maybe you need a DataGridView for other reasons?
Make your columns DataGridViewComboBoxColumn. This way every cell in these columns will be a DataGridViewcomboBoxCell.
Property DataGridViewComboBoxColumn.DataSource and DataGridViewComboBoxCell.DataSource contain all possible values. Initially set your column DataSource to all initial values, after your cells are created, you can limit the values in the Cells datasource.
class SudokuRow
{
public int C0 {get; set;}
public int C1 {get; set;}
public int C2 {get; set;}
public int C3 {get; set;}
public int C4 {get; set;}
public int C5 {get; set;}
public int C6 {get; set;}
public int C7 {get; set;}
public int C8 {get; set;}
}
Use visual studio designer to add some columns in a dummy project to see which
properties should be filled
DataGridView dgvSudoku = new DataGridView()
for (int i=0; i<9; ++i)
{
DataGridViewComboBoxColumn column = new DataGridViewComboBoxColumn();
column.Name = "column" + i.ToString();
column.DataPropertyName = "C" + i.ToString();
...
column.DataSource = enumerable.Range(1,9).ToList(); // allowed values
dgvSudoko.Columns.Add(column);
}
If you want to add a blank, then don't add numbers, but the character 0..9 and blank.
The sudokuRow contains char instead of int.
If you use DataBinding to display the items, and you want it one-direction only, you can use a List to assign to the DataSource. However, if you want to read operator input, you'll have to put the data in a BindingList.
private BindingList<SudokuRow> SudokuData
{
get => (BindingList<SudokuRow>)this.dgvSudoku.DataSource;
set => this.dgvSudokuy.DataSource = value;
}
Now initialize your datagridview:
List<SudokuData> initialSudokuData = ...
this.SudokuData = initialSudokuData;
After some operatorinput you want to limit the possibilities of a cell:
LimitPossibilites(DataGridViewComboBoxCell cell, IEnumerable<int> allowedValues)
{
// TODO: what to do if current value is not allowed?
// limit the combobox values
cell.DataSource = new BindingList<int>(allowedValues.ToList());
}
Or if you don't want to replace the BindingList with a new BindingList, consider to add / remove items using Bindling<T> properties. However, that seems to be more work.
}

Random labels in Tic Tac Toe simulation

I am working on a Tic Tac Toe simulator for a class and have run into an issue.
I created a 2-dimensional array to simulate the board and populate it with either 0 or 1 in all the boxes.
The issue I am having is getting those numbers to apply to the labels I have created (a1, a2, a3, b1, b2, etcetera).
Is there a way that my nested for loops can have each element in the array apply to a new label? I can't seem to find anything in my book or online about this.
Here is my related code:
private void newBTN_Click(object sender, EventArgs e)
{
Random rand = new Random();
const int ROWS = 3;
const int COLS = 3;
int [,] board = new int[ROWS, COLS];
for (int row = 0; row < ROWS; row++)
{
for (int col = 0; col < COLS; col++)
{
board[row, col] = rand.Next(1);
}
}
}
What are the names of the labels? I assumed below that the labels are Label0_0, Label0_1, Label1_1 and so on... This way you can find them using the row and column values.
You want to find the Label control on your form dynamically, because you don't know the name in advance while coding.
If you know the name in advance you just say: label1.Text = "1";.
But in your case, you are trying to find a particular control in each iteration of the loop. So you need to have a name for the labels so you can find them using Form.Controls.Find(string, bool) like this:
var row = 4;
var col = 6;
var l = this.Controls.Find("Label" + row.ToString() + "_" + col.ToString(), false).FirstOrDefault() as Label;
if (l == null)
{
//problem... create label?
l = new Label() { Text = "X or O" }; //the position of this need to be set (the default is 0,0)
this.Controls.Add(l);
}
else
{
l.Text = "X or O";
}
Your board stores integers, which is an internal representation of your game state. You can create a UniformGrid that holds Label for your game GUI. The code below returns a grid based on your current board. You need to add this returned grid to your MainWindow (or whatever you use) to see it.
private UniformGrid fillLabels(int[,] board)
{
int numRow = board.GetLength(0);
int numCol = board.GetLength(1);
UniformGrid g = new UniformGrid() { Rows = numRow, Columns = numCol };
for (int i = 0; i < numRow; i++)
{
for (int j = 0; j < numCol; j++)
{
Label l = new Label();
l.Content = (board[i, j] == 0) ? "O" : "X";
Grid.SetRow(l, i);
Grid.SetColumn(l, j);
g.Children.Add(l);
}
}
return g;
}
First, do not re-create (and re-initialize) Random each time you need it: it makes generated sequences skewed badly:
private static Random s_Rand = new Random();
Try not implement algorithm in the button enent directly, it's a bad practice:
private void CreateField() { ... }
private void newBTN_Click(object sender, EventArgs e) {
CreateField();
}
putting all together:
private static Random s_Rand = new Random();
private void ApplyLabelText(String name, String text, Control parent = null) {
if (null == parent)
parent = this;
Label lb = parent as Label;
if ((lb != null) && (String.Equals(name, lb.Name))) {
lb.Text = text;
return;
}
foreach(Control ctrl in parent.Controls)
ApplyLabelText(name, text, ctrl);
}
private void CreateField() {
for (Char row = 'a'; row <= 'c'; ++row)
for (int col = 1; col <= 3; ++col)
ApplyLabelText(row.ToString() + col.ToString(), s_Rand.Next(1) == 0 ? "O" : "X");
}
private void newBTN_Click(object sender, EventArgs e) {
CreateField();
}
How about you skip the INTEGER board and go directly to a Label array?
You can then do the following to loop trough all of them:
Label[,] listOfLabels; // Do also initialize this.
foreach(Label current in listOfLabels)
{
current.Text = _rand.Next(2) == 0 ? "0" : "X";
}

C# - How to access property of an array object?

I dont know how to show clearly, so:
Example - I create an array of button like this:
Button[,] _button = new Button[3, 3];
public MainPage()
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
_button[i, j] = new Button();
_button[i, j].Name = "btn" + i.ToString() + j.ToString();
_button[i, j].Tag = 0;
//Add Click event Handler for each created button
_button[i, j].Click += _button_Click;
boardGrid.Children.Add(_button[i, j]);
Grid.SetRow(_button[i, j], i);
Grid.SetColumn(_button[i, j], j);
}
} // end MainPage()
private void _button_Click(object sender, RoutedEventArgs e)
{
Button b = (Button)sender;
if (...)
b.Tag = 1;
else
b.Tag = 2;
}// end Click Event
Now how can I compare the Tag of 2 buttons in that array like:
b[1,1].Tag == b[1,2].Tag ? ...<do st>... : ....<do st>...
If you need to find position of control in array consider to set Control.Tag to that position instead of search:
_button[i, j].Tag = new System.Drawing.Point{ X = j, Y = i};
And instead of searching just
Point position = (Point)((Button)sender).Tag;
Or if you need more information (like Position + 0/x/empty choice you have) - have custom class to hold all information you need:
enum CellState { Empty, Player1, Player2 };
class TicTacToeCell
{
public Point Position {get;set;}
public CellState State {get;set;}
}
Now when you have position and state - use _buttons array to index access other ones:
Check same row:
Point position = (Point)((Button)sender).Tag;
int player1CountInThisRow = 0;
for (var col = 0; col < 3; col++)
{
if (((TicTacToeCell)(_button[position.Y, col].Tag).State == CellState.Player1)
{
player1CountInThisRow ++;
}
}
This is more of a long-winded clarification than a definite answer, but it may uncover what you're really trying to do:
In the code that you show b is (presumably) a single Button, not an array of buttons. Do you mean:
_button[1,1].Tag == _button[1,2].Tag ? ...<do st>... : ....<do st>...
Or are you trying to compare b (the event sender) to a button relative to it in the array?

c#: Creating a 10x10 field (and array) of CheckBoxes

I have a problem, and i don't know why but i can't figure out what it actually is.
All I want to do is create a 10 x 10 field of checkboxes as a board for a game basically.
The thing I came up with should use an Array of these boxes to easily identify them from box[0,0] to box[9,9] according to theirt coordinates in the field.
This is the code i am struggling with:
private void Form1_Load(object sender, EventArgs e)
{
// == OPTIONS ========================================
int xpos = 60; // Position of the first Checkbox (x)
int ypos = 60; // Position of the first Checkbox (y)
int size = 10; // Number of Rows and Columns
int spc = 30; // Space between boxes (Default:20)
// ====================================================
//other Variables
int x, y;
//Creating the Game Field
CheckBox[,] box = new CheckBox[size,size];
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
box[i, j] = new CheckBox();
//For Debugging Purpuses: Showing i and j next to checkBox
box[i, j].Text = i + "" + j;
//Set Position
y = i * spc + xpos;
x = j * spc + ypos;
box[i, j].Location = new Point(x,y);
this.Controls.Add(box[i, j]);
}
}
}
All I get from this is one single column of checkboxes marked [0,0] to [0,9].
And even if i switch around x and y or i and j, that never changes. So i.E. i will never get a row of checkboxes, just always a single column. Where am I going wrong with this?
i just get those 10 checkboxes, nothing more. they do not seem to be placed over each other either.
Hope you can help me out here :) thanks.
timo.
The check-boxes are too wide by default.
Try it with (for example):
int spc = 50;
And also add this in the loop:
box[i, j].Width = 40;

Issue while creating Nested TableLayoutPanel

I am try to implement the Nested TableLayoutPanel. I am try to dynamically Create/load the child TableLayoutPanel inside parent TableLayoutPanel.
for this I take the parent TableLayoutPanel and draw it from visual studio toolbox.
one DropDownList for dynamically to create child TableLayoutPanel I assign some values to dropdownlist such as 2*2,2*3,3*3,4*4 when the selected index change is fire is draws the child TableLayoutPanel.
My code is below
private void cmbRowsColumns_SelectedIndexChanged(object sender, EventArgs e)
{
var selectedPair = new KeyValuePair<string, string>();
selectedPair = (KeyValuePair<string, string>)cmbRowsColumns.SelectedItem;
string[] rowcolumn = selectedPair.Value.Split('*');
string strRowCount = rowcolumn[0];
int rowCount = Convert.ToInt32(strRowCount);
string strColumnCount = rowcolumn[1];
int columnCount = Convert.ToInt32(strColumnCount);
DynamicallyGenerateColumn(rowCount, columnCount);
}
private void DynamicallyGenerateColumn(int rowCount, int columnCount)
{
parentTableLayoutPanel.Controls.Clear();
parentTableLayoutPanel.ColumnStyles.Clear();
parentTableLayoutPanel.RowStyles.Clear();
parentTableLayoutPanel.ColumnCount = columnCount;
parentTableLayoutPanel.RowCount = rowCount;
for (int i = 0; i < columnCount; i++)
{
parentTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
for (int j = 0; j < rowCount; j++)
{
if (i == 0)
{
parentTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
}
TableLayoutPanel objTableLayoutPanel = new TableLayoutPanel();
parentTableLayoutPanel.Controls.Add(objTableLayoutPanel, i, j);
}
}
}
but actually problem is when I create child TableLayoutPanel the formatting is not properly
I guess you want to fill each child panels So you need to add objTableLayoutPanel.Dock=DockStyle.Fill;
TableLayoutPanel objTableLayoutPanel = new TableLayoutPanel();
objTableLayoutPanel.Dock = DockStyle.Fill;
parentTableLayoutPanel.Controls.Add(objTableLayoutPanel, i, j);

Categories