It's a bit difficult for me to describe, but this pseudo C# should explain what I'm trying to do.
There is a large number of labels on a windows form.
I'd like to change the text colour of some of those labels.
private void allBlackLabels()
{
int[] lray = { 1, 2, 3, 5, 6 };
foreach (int i in lray)
{
label[i].Forecolor = Color.Black;
}
}
I hope that explains what I am trying to do here.
Now it's obvious it won't work due to the label[i], but how would I get around this?
It might not work, because your Labels aren't held in an array. The following code will work, considering you know the names of the label to be changed:
Label[] lray = { labelOne, labelDifferent, labelWhatElse };
foreach (Label label in lray)
{
label.ForeColor = Color.Black;
}
That would work fine if you actually had an array of labels.
If you've only got lots of variables, like this:
private Label label1;
private Label label2;
private Label label3;
private Label label4;
private Label label5;
then it's harder. Options:
Change the code to use an array instead. Not as nice in the designer, but more logical
Use Controls.Find with each ID
Iterate over Controls to find all Label controls, regardless of name
foreach (Control c in this.Controls)
{
if (c is Label)
{
// do stuff
}
}
That'll grab all of the form's child controls (provided this code is in the form's code-behind) and check to see if they're labels. Just do any manipulation you want in place of the comment.
You could create the array yourself. This involves a bit of maintenance cost/risk each time your form changes though. Note: you probably want to put the array creation bit in a Form Loaded event, not a constructor, or at least, after the InitializeComponent call, so your controls are setup.
Or you could iterate on all the children of your Form (this.Controls from a Form method), mark your labels with a specific Tag, or get all Labels if that's your purpose here.
Please note that the first solution is probably much faster in runtime performance terms if you have many controls in your form.
private void allBlackLabels()
{
int[] lray = { 1, 2, 3, 5, 6 };
foreach (int i = 0; i < lray.Length; i++)
{
((Label)this.Controls["label" + i]).ForeColor = Color.Black;
}
}
If you want to achieve this for all labels on the form then something like this may help:
foreach (Control control in this.Controls) {
if (control is Label) {
(control as Label).Forecolor = Color.Black;
}
}
However if you only need to change a subset of all the labels then you need to either store the names or the indexes of the labels which need changing. This is because this.Controls has two indexers, an int32 and a string one. Thus you could try something like this
private void allBlackLabels()
{
int[] lray = { 1, 2, 3, 5, 6 };
foreach (int i in lray)
{
this.Controls[i].Forecolor = Color.Black;
}
}
It is defiantly worth noting however that the ordering in this.Controls will most likely not be as liniear looking as your lray array.
Hope this helps
Try some thing like this:
List<string> ListLabelNames = new List<string>() { /* Your label name list*/ };
foreach (Label TmpLabel in this.Controls.OfType<Label>())
{
foreach (string strTmp in ListLabelNames)
{
if (String.Compare(TmpLabel.Name, strTmp, true) == 0)
TmpLabel.ForeColor = System.Drawing.Color.Black;
}
}
Hope this helps.
I noticed that you excluded 4 from the "lray" array in your example. If you are looking to exclude one or more labels from being automatically updated by code (perhaps to highlight one among a group), try this.
private void allBlackLabels()
{
foreach (Control control in this.Controls)
{
if (control is Label && control.Name != "label4")
{
(control as Label).Forecolor = Color.Black;
}
}
}
in pseudo c#:
List<String> controlsToChange =new List<String>{"lable1Name","lable2Name"};
foreach(Control control in form.Controls)
{
if(control.GetType().Equals(typeof(Lable))
{
if( controlsToChange.Contains(control.Name)
{
control.Forecolor=Colour.Black;
}
}
}
Related
I've created a series of Panels within a placeholder, inside each panel is 5 textboxes.
On a button press, I want to get the .Text from each Textbox, and the ID of the Panel. I planned to use a JaggedArray to keep track of all Panels and their internal TextBoxes.
string[][] jaggesaddress = new string[stop][]; //stop is an int and equal to amount of panels on screen.
String[] numbers = new String[stop];
int a = 0;
//Control ctrl;
foreach (Control ctrl in BookingPlaceholder.Controls) //BookingPlaceholder is placehodler ID
{
if (ctrl is Panel)
{
numbers[a] = ctrl.ID;
a++;
//ctrl.ID = numbers[a];
foreach(TextBox tb in ctrl.ID) // error here
{
}
}
}
However the second foreach loop has an error, and I am not 100% sure how to overcome this.
"Cannot convert type 'char' to 'System.Web.UI.WebControls.TextBox'"
I know that the numbers[a] does find the first panel, however it's a string with no extension of ID.
Any suggestions are appreciated.
Josh
Your inner loop is trying to iterate through a string. What you wanted was...
foreach(TextBox tb in ctrl.Controls.OfType<TextBox>())
instead of
foreach(TextBox tb in ctrl.ID)
If for more generic controls available in PlaceHolder then
you need to find textbox controls. so it is safe to check the control type also.
foreach (Control ctr in ctrl.Controls)
{
if (ctr is TextBox)
{
//Do your things
// ((TextBox)ctr).Text
}
}
I've found similar answers to my question before, but not quite to what I'm trying to do...
In Visual Basic (last I used it, in 06/07) there was an "Index" property you could assign to multiple controls with the same name. I used this primarily to loop through controls, i.e.:
For i = 1 to 500
picSeat(i).Print "Hello"
Next i
Is there a way to do this in C#? I know there is a .IndexOf(), but would that really help for what I'm doing? I want to have multiple controls with the same name, just different index.
This is a Windows Form Application, and I'm using Visual Studio 2012. I am talking about controls, not arrays/lists; this was possible in VB and I was wondering if it was possible at all in C#. So I want to have, say, 30 seats in a theatre. I want to have each seat represented by a picturebox named "picSeat". VB would let me name several objects the exact same, and would assign a value to a control property "Index". That way, I could use the above loop to print "Hello" in every picture box with only 3 lines of code.
No, this feature does not exist in C#, and was never implemented in the transition from classic VB to VB.Net.
What I normally do instead is put each of the controls in question in a common parent container. The Form itself can work, but if you need to distinguish these from others of the same type a GroupBox or Panel control will work, too. Then, you access the controls like this:
foreach (var picBox in parentControl.Controls.OfType<PictureBox>())
{
// do something with each picturebox
}
If you want to use a specific control, just write by name:
pictureBox6.SomeProperty = someValue;
If you need to change a specific control determined at run-time, normally this is in response to a user event:
void PictureBox_Click(object sender, EventArgs e)
{
var picBox = sender As PictureBox;
if (picBox == null) return;
//picBox is now whichever box was clicked
// (assuming you set all your pictureboxes to use this handler)
}
If you really really want the Control Arrays feature, you can do it by adding code to create the array to your form's Load event:
PictureBox[] pictureBoxes = Me.Controls.OfType<PictureBox>().ToArray();
Are we talking WinForms here? I'm not sure, but I don't think you can have multiple controls in winforms with same name. But I vaguely recall doing something similar and the solution was to name them Button_1, Button_2 etc. Then you can iterate through all controls and get your own index.
Beware though that if you want to instanciate a separate control for each seat in a theatre, you might run into some serious performance issues :) I've done something similar to that as well and ended up drawing the whole thing on a canvas and using mouse coordinates to handle the events correctly.
You may want to check out the Uid property of controls.
(http://msdn.microsoft.com/en-us/library/system.windows.uielement.uid(v=vs.110).aspx)
You can access Control through Uid property with the following
private static UIElement FindUid(this DependencyObject parent, string uid)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count == 0) return null;
for (int i = 0; i < count; i++)
{
var el = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (el == null) continue;
if (el.Uid == uid) return el;
el = el.FindUid(uid);
if (el != null) return el;
}
return null;
}
And simply use
var control = FindUid("someUid");
I copied code from this post
If you create an indexed dictionary of your user control, it will behave pretty much the same as in VB6, though you'll not see it on the VS C# GUI. You'll have to get around the placement issues manually. Still - and most importantly -, you'll be able to refer to any instance by the index.
The following example is for 3 pieces for clarity, but of course you could automate every step of the process with appropriate loops.
public partial class Form1 : Form
{
...
Dictionary<int, UserControl1> NameOfUserControlInstance = new Dictionary<int, UserControl1>()
{
{ 1, new UserControl1 {}},
{ 2, new UserControl1 {}},
{ 3, new UserControl1 {}}
};
private void Form1_Load(object sender, EventArgs e)
{
NameOfUserControlInstance[1].Location = new System.Drawing.Point(0, 0);
NameOfUserControlInstance[2].Location = new System.Drawing.Point(200, 0);
NameOfUserControlInstance[3].Location = new System.Drawing.Point(400, 0);
Controls.Add(NameOfUserControlInstance[1]);
Controls.Add(NameOfUserControlInstance[2]);
Controls.Add(NameOfUserControlInstance[3]);
}
...
}
I like using Tags to apply any type of meta data about the controls
for (int i = 0; i< 10; ++i)
{
Button button = new Button();
button.Tag = i;
}
I'm REALLY new to C# and programming overall, so my question might be stupid in your opinion but here it is.
I have created a form which contains 7 textboxes and i want to collect text from these textboxes and add them to a list. I however get an error saying, System.Windows.Forms.TextBox is a 'type' but is used like a 'variable". What should I do?
for (int i = 1; i < 8; i++)
{
if (TextBox[i].Text == "")
{
days.Add("Restday");
}
else
{
days.Add(TextBox[i].Text);
}
}
TextBox is a type. so TextBox[i] is causing you trouble.
You can alway do something like this
foreach(Control ctrl in yourform.Controls)
{
Textbox = ctrl as TextBox;
if(txtBox != null)
{
if (txtBox.Text == "")
{
days.Add("Restday");
}
else
{
days.Add(txtBox.Text);
}
}
}
This work for a basic form. If you have pannel and other container to organize your controls the approch described in Guffa answer might be better. This could also be rewritten as method who accept a collection of Control recursive use to reach all controls.
Put the textbox references in an array so that you can easily loop through them. If your textboxes are named TextBox1 to TextBox7:
TextBox[] boxes = {
TextBox1, TextBox2, TextBox3, TextBox4, TextBox5, TextBox6, TextBox7
};
foreach (TextBox box in boxes) {
if (box.Text == "") {
days.Add("Restday");
} else {
days.Add(box.Text);
}
}
I guess you don't have an array named TextBox that is why the error. You can try following:
List<strig> days = this.Controls.OfType<TextBox>
.Select(r=> string.IsNullOrWhiteSpace(r.Text)
? "Restday" : r.Text)
.ToList();
But the above would give you the textboxes added on the form directly, If these textboxes are inside other control then you can look for recursively
Go into the form editor and select one of your textboxes. Now find the properties window. If it's not visible then click View->Properties Window
One of the properties in there will be the name of the textbox control. Use that to access it's text value like so:
days.Add(txtMyTextboxName.Text);
If you must iterate through the textboxes you can do this:
foreach(var Textbox in this.Controls.OfType<TextBox>())
{
if (Textbox.Text == "")
{
days.Add("Restday");
}
else
{
days.Add(Textbox.Text);
}
}
But bear in mind this is a pretty non-standard approach and not recommended.
I have about 20 text fields on a form that a user can fill out. I want to prompt the user to consider saving if they have anything typed into any of the text boxes. Right now the test for that is really long and messy:
if(string.IsNullOrEmpty(txtbxAfterPic.Text) || string.IsNullOrEmpty(txtbxBeforePic.Text) ||
string.IsNullOrEmpty(splitContainer1.Panel2) ||...//many more tests
Is there a way I could use something like an Array of any, where the array is made of the text boxes and I check it that way? What other ways might be a very convenient way in which to see if any changes have been made since the program started?
One other thing I should mention is there is a date time picker. I don't know if I need to test around that as the datetimepicker will never be null or empty.
EDIT:
I incorporated the answers into my program, but I can't seem to make it work correctly.
I set up the tests as below and keep triggering the Application.Exit() call.
//it starts out saying everything is empty
bool allfieldsempty = true;
foreach(Control c in this.Controls)
{
//checks if its a textbox, and if it is, is it null or empty
if(this.Controls.OfType<TextBox>().Any(t => string.IsNullOrEmpty(t.Text)))
{
//this means soemthing was in a box
allfieldsempty = false;
break;
}
}
if (allfieldsempty == false)
{
MessageBox.Show("Consider saving.");
}
else //this means nothings new in the form so we can close it
{
Application.Exit();
}
Why is it not finding any text in my text boxes based on the code above?
Sure -- enumerate through your controls looking for text boxes:
foreach (Control c in this.Controls)
{
if (c is TextBox)
{
TextBox textBox = c as TextBox;
if (textBox.Text == string.Empty)
{
// Text box is empty.
// You COULD store information about this textbox is it's tag.
}
}
}
Building on George's answer, but making use of some handy LINQ methods:
if(this.Controls.OfType<TextBox>().Any(t => string.IsNullOrEmpty(t.Text)))
{
//Your textbox is empty
}
public void YourFunction(object sender, EventArgs e) {
string[] txtBoxArr = { textBoxOne.Text, textBoxTwo.Text, textBoxThree.Text };
string[] lblBoxArr = { "textBoxOneLabel", "textBoxTwoLabel", "textBoxThreeLabel" };
TextBox[] arr = { textBoxOne, textBoxTwo, textBoxThree };
for (int i = 0; i < txtBoxArr.Length; i++)
{
if (string.IsNullOrWhiteSpace(txtBoxArr[i]))
{
MessageBox.Show(lblBoxArr[i] + " cannot be empty.");
arr[i].Focus();
return;
}
}
}
What is the best way to dynamically modify the forecolor and background color of every control of a WinForm application consisting of buttons, toolstrips, panels, etc? Is there an easy way to cycle through each control automatically or do I have to manually change each one? Thanks.
You can cycle through controls, I believe that all controls have a Controls property that is a list of contained controls.
Hypothetical function:
public void ChangeControlsColours(Controls in_c)
{
foreach (Control c in in_c)
{
c.BackColor = Colors.Black;
c.ForeColor = Colors.White;
if (c.Controls.length >0 ) //I'm not 100% this line is correct, but I think you get the idea, yes?
ChangeControlsColours(c.Controls)
}
}
foreach (Control c in MyForm.Controls) {
c.BackColor = Colors.Black;
c.ForeColor = Colors.White;
}
It really depends on what you're trying to do. The most elegant way might be a linked application setting you define on design time and you're then be able to change on run time.
private void UpdateInternalControls(Control parent)
{
UpdateControl(parent, delegate(Control control)
{
control.BackColor = Color.Turquoise;
control.ForeColor = Color.Yellow;
});
}
private static void UpdateControl(Control c, Action<Control> action)
{
action(c);
foreach (Control child in c.Controls)
{
UpdateControl(child, action);
}
}