I have 6 text boxes in 6 lines, 36 in all. Line one, box one is called L1N1, line one, box two is L1N2 etc. I want to dynamically assign values to these text boxes using a string...can this be done in C#? E.g.
private void Generate_Click(object sender, EventArgs e)
{
int[,] numbers = new int[6, 6];
int lin = 0;
while (lin < 6)
{
lin++;
int num = 0;
while (num < 6)
{
num++;
Random random = new Random();
int randomNum = random.Next(1, 45);
"L" + lin + "N" + num /* <--here is my string (L1N1) i want to
set my textbox(L1N1).text to a value
randomNum!*/
Sure, the WebPage has a nice FindControl for you:
TextBox tx = FindControl("L" + lin + "N" + num) as TextBox;
List<TextBox> listOfTextBoxes = new List<TextBox>();
...initialization of list....
foreach(TextBox tb in listOfTextBoxes)
{
Random r = new Random();
tb.Text = r.Next(1,45).ToString();
}
Can't you use the FindControl API and set the .text property?
private void formMain_Load(object sender, EventArgs e)
{
this.Controls.Find("Your Concatenated ID of control ex: L1N2", true);
}
or
foreach(var cont in form.Controls)
{
if(cont is TextBox) dosmth;
}
if it is webform: (just find out the form index)
protected void Generate_Click(object sender, EventArgs e)
{
foreach(Control item in Page.Controls[3].Controls)
{
if(item.GetType().ToString().ToLower().Contains("textbox"))
{
Random rnd = new Random();
TextBox txt = (TextBox)item;
System.Threading.Thread.Sleep(20);
txt.Text = rnd.Next(1, 45).ToString();
}
}
}
Form.Controls has a FindByName method, so you could build the control name and use that. It's not brill though.
Personally I'd either create the controls programatically from TextBox[6,6], or at a push
load references to them from Form.Controls into an array[6,6] as a one off.
Once you have the array you can find all sorts of uses for it.
I echo Jon Skeet's sentiments. If you want to be able to locate a control by some row/column system, the easiest way to do so is to put the controls themselves into a collection that allows them to be indexed in a similar way:
var textboxes = new TextBox[6][];
textboxes[0] = new TextBox[]{txtL1N1, txtL1N2, txtL1N3, txtL1N4, txtL1N5, txtL1N6};
//create the rest of the "lines" of TextBoxes similarly.
//now you can reference the TextBox at Line X, number Y like this:
textboxes[X-1][Y-1].Text = randomNum.ToString();
Now, if you REALLY wanted to access these textboxes by some string value, you would use the Tag property, which is a general-purpose property of most Controls. You could also name all your textboxes according to some system. Then, a little Linq can get you the textbox you want:
this.Controls.OfType<TextBox>()
.Single(t=>t.Tag.ToString() == "L1N1").Text = randomNum.ToString();
However, understand this will be VERY slow; you'll be searching the full collection of all controls that exist on the Form every time you want one. Indexing will be much faster.
Related
I've been trying to add a few candles to an array so I can use that array for the rest of my code, using the candles' properties more easily. However it doesn't seem as my code is correct and I would love someone to help me with this. (Color != DarkGoldenrod differentiates the candles from the other labels in my project, all of which have the same color)
private Label[] CandlesToMatrix()
{
Label[] candles = new Label[7];
foreach (Control ctrl in this.Controls)
{
if ((ctrl is Label) && (ctrl.BackColor != Color.DarkGoldenrod))
{
for (int i = 0; i < 7; i++)
{
candles[i] = (Label)ctrl;
}
}
}
return candles;
}
The problem you are facing is that you assign each element in the array the control that matches the criteria.
The code runs like: Enumerate all controls and check if it is a label and not some specific color. If he finds one, he will fill up the whole array with a reference to that control. If the array was already filled by the previous match, it will be overwritten.
So you end up with an array filled with either a null or the last matching control.
I think you would like to have the array filled with 'unique' controls. So every time you find a match, you have to increase the index to write it to.
For example:
private Label[] CandlesToMatrix()
{
Label[] candles = new Label[7];
// declare a variable to keep hold of the index
int currentIndex = 0;
foreach (Control ctrl in this.Controls)
{
if ((ctrl is Label label) && (label.BackColor != Color.DarkGoldenrod))
{
// check if the currentIndex is within the array. Never read/write outside the array.
if(currentIndex == candles.Length)
break;
candles[currentIndex] = label;
currentIndex++;
}
}
}
I'll add another example......
C# has so much more to offer. This is a little old c programming style. Fixed size arrays etc. In C# you have also a List which uses the type you use between the < and >. For example. List<Label>.
Here is an example which uses a List.
private Label[] CandlesToMatrix()
{
List<Label> candles = new List<Label>();
// int currentIndex = 0; You don't need to keep track of the index. Just add it.
foreach (Control ctrl in this.Controls)
{
if ((ctrl is Label label) && (label.BackColor != Color.DarkGoldenrod))
{
candles.Add(label);
}
}
return candles.ToArray(); // if you still want the array as result.
}
and... you could also use Linq (which is a next step)
private Label[] CandlesToMatrix()
{
return this.Controls
// check the type.
.OfType<Label>()
// matches it the creteria?
.Where(label => ((ctrl is Label label) && (label.BackColor != Color.DarkGoldenrod))
// fillup an array with the results
.ToArray();
}
I'm working on something for school, just a basic score calculator. I know it's not the prettiest code, but it works and that's more what the class is focused on here at the beginning.
The only issue that I have is that whenever I click "Display" it prints out 20 0s. 20 is the length of the array. Everything else is working. It adds the number I input into the array and replaces the 0s. But I don't want it to have 0s at all unless I specifically type them in.
Any help is appreciated.
Full code:
// Creates the list that displays the score
List<string> scoreList = new List<string>();
// Array to store up to 20 scores
int[] scoreArray = new int[20];
// class level variable to store current open slot in the array
int openSlot = 0;
public Form1()
{
InitializeComponent();
}
// Initializes variables that hold our math total and count of numbers entered
int total = 0;
int count = 0;
private void btnExit_Click(object sender, System.EventArgs e)
{
this.Close();
}
private void btnAdd_Click(object sender, System.EventArgs e)
{
if (openSlot <= scoreArray.GetUpperBound(0))
{
try
{
// Basic math for conversion of entered number and calculating total numbers entered
// and the averages of those numbers
int score = Convert.ToInt32(txtScore.Text);
total += score;
count += 1;
int average = total / count;
txtScoreTotal.Text = total.ToString();
txtScoreCount.Text = count.ToString();
txtAverage.Text = average.ToString();
txtScore.Focus();
}
catch(System.FormatException) // Makes sure that the user enters valid character into box
{
MessageBox.Show("Please enter valid number into box");
return;
}
// Adds the most recent entered number to the Score List
scoreList.Add(txtScore.Text);
}
// if statement to make sure that there is still room in the array to store the
// new entry
if (openSlot > scoreArray.GetUpperBound(0)) // GetUpperBound(0) returns the index of the last element in the first dimension
{
MessageBox.Show("The array is full! The most recent number was not added.");
txtScore.SelectAll();
txtScore.Focus();
return;
}
// Assigns a variable as an integer from the score text box
// to allow us to numerically sort the numbers in the scoreArray
int scoreParse = Int32.Parse(txtScore.Text);
// move the most recent number to the current open slot in the score array
scoreArray[openSlot] = scoreParse;
// add 1 to openSlot
openSlot += 1;
}
private void btnClear_Click(object sender, EventArgs e)
{
// Clears all input fields and resets variables to 0
openSlot = 0;
total = 0;
count = 0;
txtScore.Text = "";
txtScoreTotal.Text = "";
txtScoreCount.Text = "";
txtAverage.Text = "";
txtScore.Focus();
// Clears the array and list
int[] clearScoreArray = new int[20];
scoreArray = clearScoreArray;
List<string> clearScoreList = new List<string>();
scoreList = clearScoreList;
}
private void btnDisplay_Click(object sender, EventArgs e)
{
// If array has no stored values, display a MessageBox that informs user
if (scoreArray == null || scoreArray.Length == 0)
{
MessageBox.Show("There are no numbers to display");
return;
}
//move focus to the code textbox
txtScore.Focus();
// Creates a blank string variable named scr to input the scores into
// for the MessageBox
string scr = "";
foreach (var scoreAdded in scoreArray)
{
// Adds variable scr as the string to display
scr += scoreAdded + "\n";
}
// Sorts the array from lowest to highest number
Array.Sort(scoreArray);
// Displays a message box with the scores that were added
MessageBox.Show(scr);
}
}
When you declare a set amount of arrays (In your place it's 20), they get some sort of value, normally it's 0. Keep this is mind when you are using myArrayHere.length, cause it will check how many arrays have been declared (int[] array) and initialized (... = new array[]), not how many have been modified (given a value).
Best solution, IMO is creating a function, that would know how many elements in an array you will need or how many of them you are using (just have a function, that returns the amount of used variables, by checking it with a loop, and then modifying it later on... but that is one way to fix this issue, and there are better fixes than these I pointed out, but as I see you are newer to C# (prob.) fixing your issues with ok'ish code is good, as your first project should be meant for learning, later on, you can improve it and heck, if you are looking to become a pro, take some Programming courses on how to improve your code in general).
Good luck!
-Normantas
You can use nullable if you dont want zero as default value.
int? [] array = new int?[20] ;
Welcome to SO!
When an object is initially declared there is a default initialization value. In this case, 0 is the default value for an int in C#. The default value can often be changed at the initialization of the object if there's a supporting constructor.
When the int[] scoreArray = new int[20]; is declared, all 20 variables are assigned the value of 0. This is because C# does not allow uninitialized variables.
This link shows all default initialization values for C#.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/default-values-table
I'm making a sudoku in Windows Form Application.
I have 81 textboxes and I have named them all textBox1a, textBox1b... textBox2a, textBox2b...
I want to make it so that if any of the textboxes, in any of the rows, is equal to any other textbox in the same row, then both will get the background color red while the textboxes are equal.
I tried using this code just for test:
private void textBox1a_TextChanged_1(object sender, EventArgs e)
{
while (textBox1a.Text == textBox1b.Text)
{
textBox1a.BackColor = System.Drawing.Color.Red;
textBox1b.BackColor = System.Drawing.Color.Red;
}
It didn't work, and I don't know where I should put all this code, I know I shouldn't have it in the textboxes.
Should I use a code similar to this or is it totally wrong?
You want to iterate over the collection of text boxes just once, comparing it to those that haven't yet been compared against. If you have your textboxes in an array (let's call it textBoxes), and know which one was just changed (e.g. from the textChanged handler), you could do:
void highlightDuplicates(int i) // i is the index of the box that was changed
{
int iVal = textBoxes[i].Text;
for (int j = 0; j < 82; j++)
{
// don't compare to self
if (i == j) return;
if (textBoxes[j].Text == iVal)
{
textBoxes[i].BackgroundColor = System.Drawing.Color.Red;
textBoxes[j].BackgroundColor = System.Drawing.Color.Red;
}
}
}
If you wanted to get fancier, you could put your data in something like: Dictionary<int, TextBox>, where the key is the value and the TextBox is a reference to the text box with that value. Then you can quickly test for duplicate values with Dictionary.Contains() and color the matching text box by getting its value.
I think your current code would result in an infinite loop. The textboxes' values can't change while you are still in the event handler, so that loop would never exit.
If all of your boxes are named according to one convention, you could do something like this. More than one input can use the same handler, so you can just assign this handler to all the boxes.
The following code is not tested and may contain errors
private void textBox_TextChanged(object sender, EventArgs e){
var thisBox = sender as TextBox;
//given name like "textBox1a"
var boxNumber = thisBox.Name.SubString(7,1);
var boxLetter = thisBox.Name.SubString(8,1);
//numbers (horizontal?)
for(int i = 1; i<=9; i++){
if(i.ToString() == boxNumber)
continue; //don't compare to self
var otherBox = Page.FindControl("textBox" + i + boxLetter) as TextBox;
if (otherBox.Text == thisBox.Text)
{
thisBox.BackColor = System.Drawing.Color.Red;
otherBox.BackColor = System.Drawing.Color.Red;
}
}
//letters (vertical?)
for(int i = 1; i<=9; i++){
var j = ConvertNumberToLetter(i); //up to you how to do this
if(j == boxLetter)
continue; //don't compare to self
var otherBox = Page.FindControl("textBox" + boxNumber + j) as TextBox;
if (otherBox.Text == thisBox.Text)
{
thisBox.BackColor = System.Drawing.Color.Red;
otherBox.BackColor = System.Drawing.Color.Red;
}
}
}
I believe you will be more effective if create an Array (or a List) of Integers and compare them in memory, against compare them in UI (User Interface).
For instance, you could:
1) Create an Array of 81 integers.
2) Everytime the user input a new number, you search for it in that Array. If found, set the textbox as RED, otherwise, add the new value to that array.
3) The ENTER event may be allocated fot the entire Textboxes (utilize the Handles keyword with all Textboxes; like handles Text1.enter, Text2.enter, Text3.enter ... and so forth)
Something like:
int[] NumbersByUser = new int[81];
Private Sub Textbox1.Enter(sender as object, e as EventArgs) handles Textbox1.Enter, Textbox2.Enter, Textbox3.enter ...
int UserEntry = Convert.ToInt32(Sender.text);
int ValorSelecionado = Array.Find(NumbersByUser, Z => (Z == UserEntry));
if (ValorSelecionado > 0) {
Sender.forecolor = Red;
}
else
{
NumbersByUser(Index) = UserEntry;
}
You should have a 2 dimensional array of numbers (could be one dimensional, but 2 makes more sense) let's assume its called Values. I suggest that you have each textbox have a incrementing number (starting top left, going right, then next row). Now you can do the following:
All TextBox Changed events can point to the same function. The function then takes the tag to figure out the position in the 2dim array. (X coordinate is TAG % 9 and Y coordinate is TAG / 9)
In the callback you can loop over the textboxes and colorize all boxes as you like. First do the "check row" loop (pseudo code)
var currentTextBox = ((TextBox)sender)
var x = ((int)currentTextBox.Tag) % 9
var y = ((int)currentTextBox.Tag) / 9
// First assign the current value to the backing store
Values[currentTextBox] = int.parse(currentTextBox.Text)
// assuming variable x holding the column and y holding the row of current box
// Array to hold the status of a number (is it already used?)
bool isUsed[9] = {false, false, ...}
for(int col = 0; col <= 9; i++)
{
// do not compare with self
if(col == x) continue;
isUsed[textBox] = true;
}
// now we have the status of all other boxes
if( isUsed[Values[x,y]] ) currentTextBox.Background = Red else currentTextBox.Background = Green
// now repeat the procedure for the column iterating the rows and for the blocks
I would suggest a dynamic approach to this. Consider each board item as a cell (this would be it's own class). The class would contain a numeric value and other properties that could be useful (i.e. a list of possible values).
You would then create 3 collections of the cells, these would be:
A collection of rows of 9 cells (for tracking each row)
A collection of columns of 9 cells (for tracking each column)
A collection of 3x3 cells
These collections would share references - each cell object would appear once in each collection. Each cell could also contain a reference to each of the 3 collections.
Now, when a cell value is changed, you can get references to each of the 3 collections and then apply a standard set of Sudoku logic against any of those collections.
You then have some display logic that can walk the boards of cells and output to the display (your View) your values.
Enjoy - this is a fun project.
i.e for instance consider this sentence. "This is my sentence." I want the program to display highlighting first 'This' then 'is' and so on. can this actually be done? should i use a timer ? help with brief explanation is greatly appreciated. Thanks in advance.
A timer is a good option if you don't want the UI to be blocked all the time. A very basic solution for your problem is the following:
Add this to your initialization code:
// index of highlighted text block
var i = 0;
var timer = new Timer()
{
Interval = 300
};
timer.Tick += new EventHandler((sender, e) =>
{
// split the elements to highlight by space character
var textElements = this.richTextBox1.Text
.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries)
.ToArray();
// avoid dividing by zero when using modulo operator
if (textElements.Length > 0)
{
// start all over again when the end of text is reached.
i = i % textElements.Length;
// clear the RichTextBox
this.richTextBox1.Text = string.Empty;
for (var n = 0; n < textElements.Length; n++)
{
// now adding each text block again
// choose color depending on the index
this.richTextBox1.AppendText(textElements[n] + ' ', i == n ? Color.Red : Color.Black);
}
// increment the index for the next run
i++;
}
});
timer.Start();
This solution uses an extension method. To use this, you must add an extension class like this:
static class RichTextBoxExtensions
{
public static void AppendText(this RichTextBox richtTextBox, string text, Color color)
{
richtTextBox.SelectionStart = richtTextBox.TextLength;
richtTextBox.SelectionLength = 0;
richtTextBox.SelectionColor = color;
richtTextBox.AppendText(text);
richtTextBox.SelectionColor = richtTextBox.ForeColor;
}
}
You can get more information about the extension method I used here.
The drawback of this solution is that the RichTextBox is not realy useable while the highlighting is going on. If you want the user to input some text, you should stop the timer first.
Ok I got the arrays to have a fixed number to display and sum but what I'm trying to do is have the user fill all three arrays and then display them with there sum in one label if possible. Here is what I have. I have looked for information on this on Google with some results but not for C#. The reason I'm asking this question is to better myself, I have already accomplished what I was supposed to do. I would like to know how a user would create there own array and then have it displayed with their sum. Please help and I appreciate any help. Please go easy on me. I still am a beginner. If you don't understand what I'm trying to ask, please allow me to elaborate.
The Image is an example of what i want the user to be able to do in order to populate the arrays.
Here is the code for that. Im trying to basically combine them in a way the user can build the array rather then have the array already populated.
private void Submit_Click(object sender, EventArgs e)
{
Nmbrs[x] = Convert.ToInt32(UsrInputBox.Text);
UsrInputBox.ResetText();
++x;
if (x == Nmbrs.Length)
GetSmlLrg();
}
//Method
void GetSmlLrg()
{
int Sml = Nmbrs[0];
int Lrg = Nmbrs[0];
for (x = 1; x < Nmbrs.Length; ++x)
{
if (Nmbrs[x] < Sml)
Sml = Nmbrs[x];
if (Nmbrs[x] > Lrg)
Lrg = Nmbrs[x];
}
My code im trying to incorporate the ability to have the user populate the array.
int[] Nmbr1 = { 9, 5, 3, 9, 8 };
int[] Nmbr2 = { 24, 48, 90, 36, 4, 9, 2, 7 };
int[] Nmbr3 = { 84,97,63,48,12,789,2,5,8,2,54,8,6 };
//Main Button
private void Submit_Click(object sender, EventArgs e)
{
GetSum(Nmbr1);
GetSum(Nmbr2);
GetSum(Nmbr3);
}
//Method
public void GetSum(int[]Array)
{
int Total = 0;
for (int x = 0; x < Array.Length; ++x)
{
if (x == 0)
Output.Text += "\n";
Output.Text += " " + Array[x];
Total += Array[x];
}
Output.Text += "\nThe total is - " + Total + "\n"+"----------------------------------
------";
If I understand what you are trying to do, you need to modify your UI a bit.
You need your text box, submit button, and a new "Calculate" button. The calculate button indicates that input is complete, and then displays the result.
The code would look something like:
List<int> numbers = new List<int>();
private void Submit_Click(...)
{
numbers.Add(int.Parse(UsrInputBox.Text));
}
private void Calculate_Click(...)
{
int smallest = numbers.Min();
int largest = numbers.Max();
//Display code
}
Note that I use a List<T>, because you don't use arrays when the size is not known at compile-time. I also take advantage of the LINQ Min and Max functions to make the search easier. You probably haven't learned about them yet, but the LINQ extensions are very powerful tools in C#.
Let me know if this helps or if you have questions. This example doesn't have the 3 arrays you did, but you're UI doesn't show them either. If you need that kind of behavior you'll just run the above code in some sort of loop. If you provide more details I can try and flesh it out a bit for you.