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();
}
Related
I was trying to call multiple labels with multiple names from a for loop, but the thing is that i dont want to use the "foreach" to loop trough all the controls.
I want to make a direct reference to it, for example :
for(ai = 2; ai < 11 ; ai ++)
{
this.Controls("label" + ai).Text = "SomeRandomText";
}
How can i do this?
I already tried to find this question on the net, but all i find are answers with "foreach" loops.
Thanks!!
Assuming that your labels are named "lable2" through "label10", then you can do it like this:
for(int ai = 2; ai < 11 ; ai++)
{
this.Controls["label" + ai].Text = "SomeRandomText";
}
Here is a solution that is not dependent on the control's name so you are free to change the name of the label at any point in time without breaking your code.
foreach (var control in this.Controls)
{
if (control is Label)
{
int index;
if (control.Tag != null && int.TryParse(control.Tag.ToString(), out index) && index >= 2 && index < 11)
{
((Label)control).Text = "SomeRandomText";
}
}
}
Then, all you need to do is assign a value between 2 and 11 to each control's Tag property that you want updated. You can set this property through code or set the property in the designer.
You are also free to change the values of the Tag property as you see fit. Just make sure the index checks in the code line up with the tag values you choose!
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 have this code that is suppose to check each label for the word "closed", and after its done checking it will remove all the text that is in the labels and place everything thats NOT labeled "closed" into the TO section of an email. I dont know what im doing wrong but it doesnt work. Any suggestions?
foreach (Control c in Controls)
{
if (c is Label)
{
// Grab label
Label lbl = c as Label;
if (lbl.Text.Contains("closed"))
{
lbl.Text.Replace("closed", "");
}
}
}
Apparently you forgot to assign the modified text value, because Replace() method returns replaced text as return value:
lbl.Text = lbl.Text.Replace("closed", "");
But there might be more problems with your code, it's not very clear how is your problem related to emails.
Initially take your input (i.e. label list in a List)
List<string> TotalLabels = GetAllLabels();
for (int i = 0; i < TotalLabels.Count; i++)
{
if (TotalLabels[i].Contains("closed"))
{
TotalLabels.RemoveAt(i);
i--;
}
}
Now you have a final list which you wanted removing "closed".
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.
How can I get the visible items from a winforms listview? There doesn't seem to be a straightforward way, and I am reluctant to query items by control.clientrectangle or other hacks similar to the following:
https://stackoverflow.com/questions/372011/how-do-i-get-the-start-index-and-number-of-visible-items-in-a-listview)
When I say visible I mean the items are visible on the screen.
You can iterate from ListView.TopItem and check ListViewItem.Bounds property of each item whether it is located within the client area.
Better ListView Express is a freeware component that have also BottomItem property, so you can easily go through the visible items with a for loop (if both TopItem and BottomItem are not null):
for (int i = betterListView.TopItem.Index; i < betterListView.BottomItem.Index; i++)
{
// your code here
}
You can try this - it have the same interface as ListView and have many improvements over .NET ListView.
Sample code using GetItemAt
Looking at #Hans Passants comment I made a stab at actually creating code.
This code gets the top/bottom items. To get a Collection of visible items should be easy by out from Items where index is between index of top/bottom.
For me this worked much better than using Bounds, the bounds of the ListView appeared to have been higher then the visible part.
/// <summary>
/// Finds top/bottom visible items
/// </summary>
public static (ListViewItem, ListViewItem) GetTopBottomVisible(ListView listView)
{
ListViewItem topItem = listView.TopItem;
int lstTop = listView.Top;
int lstHeight = lstTop + listView.Height;
int lstBottom = lstHeight;
int step = lstHeight/2;
int x = listView.Left + listView.Width/2;
int y = lstTop + step;
ListViewItem bottomCandidate=null;
// iterate by interval halving
while ( step > 0 )
{
step /= 2; // halv interval
ListViewItem itm = listView.GetItemAt(x, y);
if ( itm == null )
{
// below last, move up
y -= step;
}
else if ( itm == bottomCandidate )
{
// Moving still in same item, stop here
break;
}
else
{
// above last, move down, storing candidate
bottomCandidate = itm;
y += step;
}
}
return (topItem, bottomCandidate);
}
If you are looking for a function that gives you only the visible item list, there is no such thing. You can go foreach item and check if its visible or not.
(If i understood your question right? Please give much clear explanation)