i made a program that create many dynamic panel on button "ADD". The thing is that the name of the dynamic panel that have been created is by this way:
pArr[counter].Name = "panel" (panel0.Controls.Count 1);
And so on, so i guess the 5th panel would be named "panel5" if I'm right.
I added some buttons and a Textbox in these dynamically created panels. I don't understand how to use them. I have read a lot of posts in here about it but as its the first time I'm working with this kind of dynamic panels, its hard for me to understand. I found a lot of examples that seems to answer the question but I'm still confused about the names, how to controls buttons and/or textbox.
Per example, how will I set an Event from a button clicked from the 7th panel? It is complicated.
This is my code:
private void btnAdd_Click(object sender, EventArgs e)
{
Color color = ColorTranslator.FromHtml("#f0f0f0");
Color color2 = ColorTranslator.FromHtml("#4285f4");
System.Random R = new System.Random();
Panel p = new Panel();
Label lbl = new Label();
TextBox text = new TextBox();
Button btn = new Button();
Button btn2 = new Button();
Button btn3 = new Button();
int x = 100;
int y = 10;
int x2 = 300;
int y2 = 10;
int x3 = 380;
int y3 = 10;
int x4 = 20;
int y4 = 13;
int x5 = 500;
int y5 = 10;
int counter = 0;
Panel[] pArr = new Panel[25];
pArr[counter] = p;
pArr[counter].Name = "panel" (panel0.Controls.Count 1);
pArr[counter].BackColor = color;
pArr[counter].Size = new Size(panel0.ClientSize.Width, 40);
pArr[counter].BorderStyle = BorderStyle.Fixed3D;
pArr[counter].Dock = DockStyle.Top;
lbl.Text = "Name";
lbl.ForeColor = color2;
lbl.Font = new Font("Verdana", lbl.Font.Size);
lbl.Size = new Size(75, 20);
lbl.Location = new Point(x4, y4);
pArr[counter].Controls.Add(lbl);
text.Name = "txtBox" (panel0.Controls.Count 1);
text.Size = new Size(120, 20);
text.Location = new Point(x, y);
pArr[counter].Controls.Add(text);
btn.Name = "btnStart" (panel0.Controls.Count 1);
btn.Text = "Start";
btn.Font = new Font("Verdana", btn2.Font.Size);
btn.ForeColor = color2;
btn.Size = new Size(75, 20);
btn.Location = new Point(x2, y2);
pArr[counter].Controls.Add(btn);
btn2.Name = "btnStop" (panel0.Controls.Count 1);
btn2.Text = "Stop";
btn2.Font = new Font("Verdana", btn2.Font.Size);
btn2.ForeColor = color2;
btn2.Size = new Size(75, 20);
btn2.Location = new Point(x3, y3);
pArr[counter].Controls.Add(btn2);
btn3.Name = "btnClose" (panel0.Controls.Count 1);
btn3.Text = "Close";
btn3.Font = new Font("Verdana", btn3.Font.Size);
btn3.ForeColor = color2;
btn3.Size = new Size(75, 20);
btn3.Location = new Point(x5, y5);
pArr[counter].Controls.Add(btn3);
panel0.Controls.Add(pArr[counter]);
panel0.AutoScroll = false;
panel0.HorizontalScroll.Enabled = false;
panel0.HorizontalScroll.Visible = false;
panel0.HorizontalScroll.Maximum = 0;
panel0.AutoScroll = true;
counter ;
}
Can someone explain me how to control panels/buttons/textbox?
CodePudding user response:
pArr needs to be a member of the class, not a local variable. Then you can simply access pArr with an index like any other array.
The panel doesn't even need a name. Names are only useful in the Visual Studio designer so that it can generate the .Designer.cs file with a variable name.
As for the events, just add an event handler with btn.Click = OnStartClick; as you would do for a button normally. In the function declaration, you will get object sender which identifies the button. You can then use Array.IndexOf() MSDN to get the index.
private void OnStartClick(object sender, EventArgs e)
{
var button = (Button) sender;
var panel = button.Parent;
var index = Array.IndexOf(pArr, panel);
...
}
CodePudding user response:
Hokay, so on a comment elsewhere you said you want to
MessageBox.Show("Button name pressed: " buttonName " panel position number: " panelPositionNb)
It means minimally you can have a code that looks like: (the second to last line is new)
btn.Name = "btnStart" (panel0.Controls.Count 1);
btn.Text = "Start";
btn.Font = new Font("Verdana", btn2.Font.Size);
btn.ForeColor = color2;
btn.Size = new Size(75, 20);
btn.Location = new Point(x2, y2);
btn.Click = (s, e) => MessageBox.Show($"Button name pressed: {btn.Name}");
pArr[counter].Controls.Add(btn);
Taking this apart:
btn.Click = (s, e) => MessageBox.Show($"Button name pressed: {btn.Name}");
.Clickis the event of the button. An event is simply a "list of methods that have a particular signature", and when you say=you add a method to the "list of methods that shall be called when the event is raised". There's no defined order; you can add multiple different methods and they will be called, but not necessarily in any particular sequence.Clickhas a type ofEventHandlerwhich sets out the number and type of arguments any method must have in order to qualify as an handler for any event that is shaped like an EventHandler=we've covered; if there is an event on the left, then the thing on the right must be a method with a particular signature (dictated by the event signature) i.e. in the case of a button your method on the right must be shaped like anEventHandlerwhich means it must have a first argument of typeobjectand a second argument of typeEventArgs. YourbtnAdd_Clickis an example of a method that conforms to those rules(s, e)is a method signature header. Specifically it's a mini method that we call a lambda. The compiler looks at the event signature and knows that the first thing has to beobjectand the second has to beEventArgs; you only have to give names - the compiler will fill in the types. Actually because we don't even use the names of the arguments in the method code, we could just write(_, _)(in .net core ; framework might complain about duplicate names) here which means "no name; throw it away".. But if we did use the variable in the lambda method we'd have to give it a name. We could give them types too:(object s, EventArgs e)and it starts to look a lot like a normal method if we do; we don't add these because mostly we don't need to; the compiler can usually figure them out on its own=>is the thing that separates a lambda method signature from the bodyMessageBox.Show($"Button name pressed: {btn.Name}")is the body of the method; the thing that will happen when the button is clicked.
Doing a panel close one:
btn3.Name = "btnClose" (panel0.Controls.Count 1);
btn3.Text = "Close";
btn3.Font = new Font("Verdana", btn3.Font.Size);
btn3.ForeColor = color2;
btn3.Size = new Size(75, 20);
btn3.Location = new Point(x5, y5);
var pnl = pArr[counter];
btn3.Click = (s,e) => pnl.Visible = false;
pnl.Controls.Add(btn3);
Here we take the panel and capture it into a temporary variable. There are specific reasons for doing this, and it doesn't exactly apply to your code as written but I'm assuming at some point you'll upgrade this to use a loop; if we just did pArr[counter].Visible C# would take the resting value of counter as it was after all the looping is done, and hide that panel.. Any button would either cause an error, because counter would be off the end of the panels array, or some other wrong panel would hide.
We don't have to use these mini-methods (lambdas); we can pass this data around another way if you like..
Taking the example of the panel hide, because it's actually doing something interesting with the UI. Let's have a method that hides a panel that it finds in the Tag of the control in the sender:
private void HidePanelInTag(object sender, EventArgs e){
//get the sender as a button; we will only ever attach this code to buttons that have a Panel in their Tag
var b = sender as Button;
//get the Tag from the button and cast it to a panel
var p = b.Tag as Panel;
p.Visible = false;
}
Now we can tweak the loop that is creating the btn3 close buttons:
btn3.Name = "btnClose" (panel0.Controls.Count 1);
btn3.Text = "Close";
btn3.Font = new Font("Verdana", btn3.Font.Size);
btn3.ForeColor = color2;
btn3.Size = new Size(75, 20);
btn3.Location = new Point(x5, y5);
btn3.Tag = pArr[counter];
btn3.Click = HidePanelInTag;
pnl.Controls.Add(btn3);
We've stored the panel in the Tag - this varies for every button, a different Panel. The event handler attachment looks simpler too, because whereas a lambda has a full method (header, and body) defined on the line it is encountered, in more old fashioned terms the method definition is separated from its use:
btn3.Click = HidePanelInTag;
Remember before we said = separates an event on the left, from a method on the right; so long as the method on the right obeys the signature of the event, we're allowed to "add it to the list of methods that are called when the event is raised".
When the button is clicked, HidePanelInTag is called. Windows Forms automatically puts the control that is raising the event (the button) into the object sender argument. That's how we retrieve the Panel to hide; every button has a different Panel in its tag. If you click the 25th button, the sender is the 25th button, and the Tag is the 25th panel
End of the day there are loads of ways to skin this cat. For example you don't have to store a panel in a button's Tag.. You could even do some crazy like this:
btn3.Click = (s, e) => {
var b = s as Button;
var pname = b.Name.Replace("btnClose", "panel");
var p = panel0.Controls[pname];
p.Visible = false;
};
Get the button name, change it to be what the panels name is, find the panel by name, hide it... All in a multi-line lambda. That's kinda nasty, just treat it "for educational purposes" - ultimately all these buttons you make end up stored in tree of controls (your form has a penal that has panels that have buttons..) that can be searched, so "name" does have a use.. But we probably wouldn't use it for this approach.
The main take-away from this is that just like setting the name, the text, the position etc dynamically you also need to wire up the click events dynamically. That's done using event_name_here = event_handler_method_name_without_parentheses_here; in this regard methods aren't really any different from data variables in the way they're passed around by name
