How do I run a function on code generated UserControl? - c#

I have created a form that has generated several UserControls. There has to be an unlimited amount that can be added - there wont necessarily, but there needs to not be a cap on the amount.
I have used the following code to add them:
for (int i = 0; i < ucCount; i++)
{
UserControl1 uc = new UserControl1();
uc.name = "uc" + i.ToString();
flowLayout.Controls.Add(uc);
uc.Click -= new EventHandler(uc_Click);
uc.Click += new EventHandler(uc_Click);
}
ucCount is simply a variable to change the amount that are added.
The problem I'm having now, is I have a method in the UserControls that needs to be run when a button is pressed. This is the code for the following method:
public void ResizeTrucks()
{
...
}
Normally, if I had added the controls with a special name, I would have done the following:
uc.ResizeTrucks();
Since I added them in the code, and changed their names, I can no longer do that.
I now have the following code to change the size of all the usercontrols.
foreach (Control c in flowTrucks.Controls)
{
c.Width = 103;
c.Height = 528;
}
Basically, the problem that I'm facing is running a function in a usercontrol that I have generated in code, so I can't just do uc1.ResizeTrucks();.
I think I remember reading somewhere that you can get the function by name and then run it, but I haven't been able to find it anywhere. Any help will be appreciated.

You want to cast to the appropriate user control type, and then you can run the function. Something like:
foreach (Control c in flowTrucks.Controls)
{
c.Width = 103;
c.Height = 528;
var x = c as UserControl1;
if (x == null) continue; // not a UserControl1
x.ResizeTrucks();
}

Related

User Control not Adding to TableLayoutPanel at Runtime

I have a TableLayoutPanel on a form which has 8 columns and 8 rows. I'm trying to populate this table from a list of a user controls which each contain three labels. When I run the app, I can see it has been populated, but looking at the actual form, it is empty.
I've tried replacing the control with just a label, and they show up fine, it's just when I try to use the user control that it doesn't work.
This code runs in the Load method and generates a list of GridSquareModel, which is used to populate the user control. Each model has an X and Y position which relates to their cell on the table panel.
var startup = new StartupForm();
if (startup.ShowDialog() == DialogResult.OK)
{
var grid = new List<GridSquareModel>();
for (int i = 0; i < 64; i++)
{
Position pos = new Position();
pos.X = i % 8;
pos.Y = i / 8;
grid.Add(new GridSquareModel(pos));
}
RenderGrid(grid);
}
else
{
Application.Exit();
}
This method generates a GridSquareControl for every GridSquareModel in the list. The X and Y value in the position determines which cell they are added to.
private void RenderGrid(List<GridSquareModel> grid)
{
UIGrid.Clear();
GridTable.Controls.Clear();
foreach (var item in grid)
{
var control = new GridSquareControl(item)
{
Dock = DockStyle.Fill
};
GridTable.Controls.Add(control, item.Position.X, item.Position.Y);
//GridTable.Controls.Add(new Label
//{
// Text = "TEST"
//}, item.Position.X + 1, item.Position.Y + 1);
}
}
I have commented my attempt to replace the GridSquareModel with a Label, which worked. This should produce a table containing 64 controls but once it is loaded the table appears empty.
I'm an idiot. I created a constructor for GridSquareControl but it did not include the InitializeComponent method, so obviously it was not rendered. All is working as expected now.

Using labels like arrays

I am working on winform application in asp.net using c#. I have 10 labels on my winform created in the designer mode, called Label0 to Label9. Now I want to change the Text property of all the labels at once as per the data I acquire in the middle of execution of my program.
i want to do something like this :
for (int i = 0; i < 10; i++)
{
Label[i].Text = "Hello, this is label: "+ i.ToString();
}
Now, of course this won't work. But how can this be done? how can i call the label like its done in an array? If not possible, then what can be the best alternative solution for my problem?
If you are talking about WinForms, then you can do like this:
private void Form1_Load(object sender, EventArgs e)
{
// Form1_Load is just sample place for code executing
for (int i = 1; i < 10; i++)
{
var label = Find<Label>(this, "label" + i);
label.Text = "Hello, this is label: " + i.ToString();
}
}
private T Find<T>(Control container, string name)
where T : Control
{
foreach (Control control in container.Controls)
{
if (control is T && control.Name == name)
return (T)control;
}
return null;
}
This code will search label in form controls, and then return it based on control name and type T. But it will use just parent form. So if your label is in some panel, then you need to specify panel as container parameter. Otherwise Find method can be updated as recursive method, so it will search inside all form subcontrols, but if there will be two Label1 controls, then it will return just first one, that might be not correct.
If you can put all Label on a panel after the you can use below code to change the text
foreach (Control p in panal.Controls)
if (p.GetType == Label)
p.Text = "your text";

Changing The Property Of An Object Using Variables? C#/Visual Studio

Ok so I have 100 buttons and I need to change there colors based on conditions in a while loop. They are named button1, button2, button3 ,etc. during the first time around the loop (iteration?) I need to edit button1, the next time button2 the third time button3, etc.
I thought I could just make a string that equaled "button", add the number of times around the loop to it and change the color like that.
String ButtonNumber = "button" + i; where i = number of times around loop
When I try to edit the color using ButtonNumber.BackColor = Color.Red; it won't let me because it's not treating ButtonNumber like a button, but like a string. How do I accomplish this? Thanks! (this is my first time programing pretty much)
Consider using Controls.Find to find a control by name, and then you can change it's properties:
for (int i = 1; i <= 100; i++)
{
var buttonName = string.Format("button{0}", i);
var foundControl = Controls.Find(buttonName, true).FirstOrDefault();
if (foundControl != null)
{
// You can now set any common control property using the found control
foundControl.BackColor = Color.Red;
// If you need to set button-specific properties (i.e. properties
// that are not common to all controls), then cast it to a button:
var buttonControl = foundControl as Button;
if (buttonControl != null)
{
buttonControl.AutoEllipsis = true;
}
}
}

Referencing a dynamically created control in a new TabPage

newbie programmer here after hours of searching has left me stumped.
I'm having trouble with referencing a control inside a tab created at RunTime with a button press. Basically what I have is a tabletop RPG calculator, using a Windows Form, that has a tabControl holding tab pages, with each tab page holding user-inputted stats for that individual enemy to be used in calculations.
The problem is that I want the user to be able to click a button to generate a new enemy tab page. Here is my code for generating an enemy tab page with a TextBox.
int enemyNumber = 0;
// Creates a new Enemy Tab
private void button2_Click_1(object sender, EventArgs e)
{
// Create a new TabPage
var newTabPage = new TabPage()
{
Text = "Enemy " + enemyNumber,
};
// Add Enemy Name Box
var newEnemyNameBox = new TextBox()
{
Name = "enemyNameBox" + enemyNumber,
Text = "",
Location = new Point(127, 11),
Size = new Size(133, 20)
};
// Add the controls to the new Enemy tab
newTabPage.Controls.Add(newEnemyNameBox);
// Add the TabPage to the TabControl
tabControl1.TabPages.Add(newTabPage);
// Increases the enemy's "reference number" by 1
// So that enemy tabs will be generated in order enemyTab0, enemyTab1, etc.
enemyNumber += 1;
}
This all works nicely. Unfortunately, after this point things have gotten ugly. I need to reference that TextBox named "enemyNameBox" + enemyNumber, and I'm not sure how to do so.
What I did was create "archVariables" to store the values from whatever enemy tab is selected, then use the appropriate archVariable in the program's calculations. IE: archEnemyName. The idea is that whatever tab the user is currently selected on (determined via SelectedIndex) the TextBox from that page will be used for the program's output.
Here are the two things I've tried after researching the matter:
// Attempt 1
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
archEnemyNameBox = ((TextBox)Controls["enemyNameBox" + i]).Text;
}
}
This code simply throws a NullReferenceException when I press the button. So after researching more I tried this:
// Attempt 2
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
TextBox tb2 = new TextBox();
tb2 = ((TextBox)(enemyTab.Controls.Find("enemyNameBox" + i, true)));
archEnemyNameBox = tb2.Text;
}
}
This time I got an Error: Cannot convert type 'System.Windows.Forms.Control[]' to 'System.Windows.Forms.TextBox'
I feel like the second method I have here is probably closer to the correct way to do this, but apparently I'm still not getting it right. I've learned a lot by searching the information on stackoverflow and msdn.microsoft but nothing has gotten me past this problem.
Any help would be appreciated.
basically the problem with your second attemp is that enemyTab.Controls.Find("enemyNameBox" + i, true) returns an array of Controls Control[] and you're trying to convert that to a Control here is the problem, you should get the first control in that array and then convert it to a Control so it should be like this:
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
TextBox tb2 = new TextBox();
tb2 = ((TextBox)(enemyTab.Controls.Find("enemyNameBox" + i, true)[0]));
archEnemyNameBox = tb2.Text;
}
}
but it is not the BestWay to do so it seems that everytime a user adds a new tabPage it will have the same Controls right? so why not create an userControl with any Control you have on your TabPage? so when you press the user press to add a new tab your code should be like so:
private void CreateNewEnemyTab()
{
var newTabPage = new TabPage()
{
Text = "Enemy " + enemyNumber,
};
EnemyTabUserControl enemyTab = new EnemyTabUserControl(enemyNumber);
here the EnemyTabUserControl should have all the components you need;
newTabPage.Controls.Add(enemyTab);
tabControl1.TabPages.Add(newTabPage);
}
and the code to bring the TextBox from the current tab could be as follow (you are going to need to reference LINQ)
using System.Linq;
//First Lets create this property, it should return the selected EnemyTabUserControl inside the tabControl
public EnemyTabUserControl CurrentTab {
get {
return tabControl1.SelectedTab.Controls.OfType<EnemyTabUserControl>().First();
}
}
// then if we make the textbox you want to reference from outside the code we can do this
CurrentTab.NameOfTheTextBox;
Patrick has solved your fundamental problem, but I don't think you need the loop in there at all. Here I've broken the steps out so you can see what needs to happen a little better:
private void defendCalcButton_Click(object sender, EventArgs e)
{
Control[] matches = this.Controls.Find("enemyNameBox" + tabControl1.SelectedIndex.ToString(), true);
if (matches.Length > 0 && matches[0] is TextBox)
{
TextBox tb = (TextBox)matches[0];
archEnemyNameBox = tb.Text;
}
}

Can I give controls an index property in C# like in VB?

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;
}

Categories