Create controls dynamically - c#

I want to know if this is possible in c# winform.
create control when ever button is pressed and place it at given location.
I think it is possible like this
private TextBox txtBox = new TextBox();
private Button btnAdd = new Button();
private ListBox lstBox = new ListBox();
private CheckBox chkBox = new CheckBox();
private Label lblCount = new Label();
but the problem lies when ever button is pressed same name controls are created.How to avoid that
What da........
i wrote and no exception i was expecting it because control already contains btnAdd instead as many button create as many you want.
Accessing them will be issue but it will be solved by #drachenstern method correct?
private void button1_Click_1(object sender, EventArgs e)
{
Button btnAdd = new Button();
btnAdd.BackColor = Color.Gray;
btnAdd.Text = "Add";
btnAdd.Location = new System.Drawing.Point(90, 25+i);
btnAdd.Size = new System.Drawing.Size(50, 25);
this.Controls.Add(btnAdd);
i = i + 10;
}

int currentNamingNumber = 0;
txtBox.Name = "txtBox" + currentNamingNumber++;
Rinse, Repeat.
Gives each element a unique numeric name, allows you to find out how many elements have been created (notice that you don't want to decrement to track all created objects, because then you may create two elements with the same name).
I don't think you can pass the name you want into the new function, but you can always set the name after creating it.

You could try the solution I posted here. It will dynamically create 5 buttons in the constructor. Just move the code to the button click event and it should add the buttons dynamically and register with the Click events.

It sounds like your looking for a List<TextBox>.

Related

Is there a way to dynamically generate code to interact with buttons in C#?

I'm trying to find a a way to be able to essentially dynamically generate code based on an input.
For example I could type something like:
int Number = 22;
Button<Number>.Text = "X";
So in this case it would set button22 to have its text be an "X".
And I could change it so that I could input, for example 24 into the program and it would then set button24 to be an "X", instead of setting up a bunch of if statements to cover every potential button press.
For further context I have a Grid of 64 buttons and I need to be able to edit them individually to show to the user which buttons have been pressed, it is possible to do it with a lot of if statements but I thought it might be worth trying to find a more elegant solution.
You could have a list of buttons:
private List<Button> _buttons = new List<Button>();
Populate it like this:
for (int i = 0; i < 10; i++)
{
var b = new Button();
b.Text = $"Button #{i}";
b.Click += HandleButtonClick;
}
And you could even set an event handler on one of its events which doesn't even need to use the list (the sender is the source of the event):
private void HandleButtonClick(object sender, EventArgs e)
{
(sender as Button).Text = "X";
}
Buttons have a Tag property that can be used to hold arbitrary data about a button, this is described for WinForms, WPF and UWP.
Simple usage that is similar to OP's requirement is demonstrated in this SO post
This situation is in a practical sense the very reason that .Tag exists at all in user interface controls pretty much from the birth of c#.
So you do not need to use a custom class for a button, just simply assign your value to the .Tag property on the Button class that you are creating programmatically:
in this example a list is used to create the buttons and separate the creation from the layout, it is not necessary to do this, but may be useful. Instead, you could assign this button to it's parent container and/or set the layout margins or coordinates without keeping a reference to the Button object at all.
If OP updates the post to include implementation examples, we can update this response with more specific and complete code.
private List<Button> _buttons = new List<Button>();
// ... iteration or switching logic
var nextButton = new Button
{
Text = "x",
Tag = 22
};
nextButton.Click += DynamicButton_Click;
_buttons.Add(nextButton);
// ... later push the buttons into the parent container or bind to the UI
Then the button click handler you can access this Tag property:
this is presented from WinForms, the only difference in UWP or WPF is the method signature, change EventArgs to RoutedEventArgs
private void DynamicButton_Click(object sender, EventArgs e)
{
if(int.TryParse((sender as Button).Tag?.ToString(), out int buttonValue))
{
// use buttonValue
Console.Out.WriteLine(buttonValue);
}
else
{
// Otherwise, sender was not a button, or the button did not have an integer tag value
// either way, handle that error state here...
}
}
Using these concepts, once the buttons are created, let's say in some simple grid alignment, you could allow the user to set this Tag value at runtime if you have a TextBox (or other) input field that can be accessed from the code.
I recommend that you use MVVM style bindings for this rather than directly referencing a TextBox control, but this is simply to demonstrate the point.
private void DynamicButton_Click(object sender, EventArgs e)
{
// assign the string value from the ButtonValueTextbox control to this button
string value = this.ButtonValueTextBox.Text;
if(sender is Button button)
{
button.Tag = value;
}
else
{
// Otherwise, sender was not a button
// handle the error state here if you need to...
}
}
Now that each button has a tag, you could easily add logic to maintain unique tag values by iterating through the other buttons and clearing the tag if it was previously assigned to a different button.
Maybe you could keep a List of Button References:
var myButtons = new List<Button>();
myButtons.Add(firstButton);
myButtons.Add(secondButton);
// ... etc
// ... then somewhere else
int number = 3;
myButtons[number].Text = "xxx";

Add dynamic labels and textboxes from a checkedListBox

I have a checkedListBox, that contains names of some chargingstations i have in a SQL database. I have filled the checkedListBox with the station names, and i would like that if you check an item in the box, a label with the text "Km to "+itemsChecked.StationName would appear just below the checkedListbox, as well as a textBox, where one would enter the kilometers to the station.
It's being used to create a new charging station, and the kilometers are the cost of the edge to the next station.
I've tried something like this:
private void stationCheckedListBox_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (BetterServiceReference.Station itemsChecked in stationCheckedListBox.CheckedItems)
{
var lbl = new Label();
lbl.Name = "lblAuto" + itemsChecked.StationId;
lbl.Text = "Km to " + itemsChecked.StationName;
lbl.AutoSize = true;
lbl.Location = new Point(33, 462);
lbl.Name = "label1";
lbl.Size = new Size(35, 13);
tabPage7.Controls.Add(lbl);
}
Only it doesn't actually create a label.
I created a sample program, and it seems to work fine for me:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (var item in checkedListBox1.CheckedItems)
{
Label lbl = new Label();
lbl.Text = "Testing";
lbl.Location = new Point(125, 125);
this.Controls.Add(lbl);
}
}
}
Some things to check / try:
Make sure the location of your label is not behind some other control!!
Use var / object instead of "BetterServiceReference.Station"
Is tabPage7 the name of your form? Try using this.Controls.Add(lbl);
I will also warn you that your function will not remove the labels once an item is unchecked. Also, as-is, it will only create one label in one spot for any checkbox checked. It's going to take a bit of work to implement what you are trying to do; as the other answer suggested, you may be better off toggling controls between visible / invisible.
But, hopefully my answer will help you dynamically create a label, and creating the textboxes will be very similar.
I think you need to create the label in the designer and make it Visible="False". Then in your code, make it Visible="True" as needed. Same with the textbox.

How to Leave ToolStripMenu Open After Clicking an Item

I'm creating a ToolStripMenu shown below that is supposed to allow the user to interact with the items "XML" and "Non XML" as though they are regular check boxes on a form. However, when one item is checked/unchecked the menu closes. How can I allow an item to be checked/unchecked without closing the menu? Or is there a different standard method of achieving the same behavior?
So what I want is to be able to click on "Non XML", show a check box and leave the menu open.
The idea is that the last menu item will be "Done" and when it's clicked the "G2S" sub items will remain open but the "Display" sub items ( XML, Non XML ) will close.
Any ideas?
Note: I am aware that this is likely not the best user interface design. I'd like to know however how this could be accomplished just to gain some technical knowledge about handling menus.
Interesting concept is described in this thread on Stackoverflow:
Here is the essence of the accepted answer:
ParentMenu.DropDown.AutoClose = false;
It does exactly what you are asking for - prevent menu from closing when subitem is clicked.
Here's a useful extension that requires user to click outside of menu item + dropdowns to close.
public static void KeepOpenOnDropdownCheck (this ToolStripMenuItem ctl)
{
foreach (var item in ctl.DropDownItems.OfType<ToolStripMenuItem>())
{
item.MouseEnter += (o, e) => ctl.DropDown.AutoClose = false;
item.MouseLeave += (o, e) => ctl.DropDown.AutoClose = true;
}
}
Posted in case somebody finds it helpful.
Instead of trying to do exactly what I had originally intended, I've come up with the following:
1- Use a ContextMenuStrip
2- When the user clicks on the ToolStripMenu item I display the ContextMenuStrip at a location near the menu item as shown below: ( note the positioning still needs adjusting )
To get this working I build the ContextMenuStrip in code at run-time so that the items in the ContextMenuStrip can be build dynamically based on the situation.
Code snippets:
Show the ContextMenuStrip when the menu item is clicked:
private void filterToolStripMenuItem_Click(object sender, EventArgs e)
{
contextMenuStrip1.Show(this, 180, 20);
}
Build the ContextMenuStrip:
if (protInfo.Name == "QCOM" )
{
BroadCast = new CheckBox();
BroadCast.Text = "Date/Time Broadcast";
BroadCast.Checked = FlagSet(CurrentFilter, (Byte)Filter.DateTimeBC);
ToolStripControlHost Ch1 = new ToolStripControlHost(BroadCast);
GenPoll = new CheckBox();
GenPoll.Text = "Status Poll";
GenPoll.Checked = FlagSet(CurrentFilter, (Byte)Filter.GenStatusPoll);
ToolStripControlHost Ch2 = new ToolStripControlHost(GenPoll);
GenPollResp = new CheckBox();
GenPollResp.Text = "Status Poll Response";
GenPollResp.Checked = FlagSet(CurrentFilter, (Byte)Filter.GenStatusResponse);
ToolStripControlHost Ch3 = new ToolStripControlHost(GenPollResp);
Button btnDone = new Button();
btnDone.Text = "Done";
ToolStripControlHost Ch4 = new ToolStripControlHost(btnDone);
btnDone.Click += new EventHandler(btnDone_Click);
contextMenuStrip1.Items.Clear();
contextMenuStrip1.Items.Add(Ch1);
contextMenuStrip1.Items.Add(Ch2);
contextMenuStrip1.Items.Add(Ch3);
contextMenuStrip1.Items.Add(Ch4);
contextMenuStrip1.Enabled = true;
filterToolStripMenuItem.Enabled = true;
}
else
{
filterToolStripMenuItem.Enabled = false;
}
This may not be the best user interface design, but it seems to work.
The original solution will work with the use of mouse events.
On mouse enter event:
parent.dropdown.autoclose = false;
on mouse leave event:
parent.dropdown.autoclose = true;
The only catch is if the user access the menu items by other means than a mouse.
I used a combination of Neolisk's and Chimera's answers to allow deletion of multiple leaf items from a treeview. My solution is below
Note: the following Items created at design time are used:
TreePromotions (TreeView)
menuVendorSection (Context Menu Strip)
removeMultipleItemsToolStripMenuItem (DropDown of menuVendorSection)
private void removeMultipleItemsToolStripMenuItem_MouseHover(object sender, EventArgs e)
{
removeMultipleItemsToolStripMenuItem.DropDownItems.Clear();
ToolStripMenuItem detailMenuItem;
TreeNode vendorSectionNode = treePromotions.SelectedNode;
for (int vsn = 0; vsn < vendorSectionNode.Nodes.Count; vsn++)
{
//add checkbox item
detailMenuItem = new ToolStripMenuItem(vendorSectionNode.Nodes[vsn].Text);
detailMenuItem.Tag = vendorSectionNode.Nodes[vsn].Tag;
detailMenuItem.CheckOnClick = true;
removeMultipleItemsToolStripMenuItem.DropDownItems.Add(detailMenuItem);
}
//add action buttons
Button buttonDeleteMultiple = new Button();
buttonDeleteMultiple.Text = "Remove Checked Items";
ToolStripControlHost buttonHost = new ToolStripControlHost(buttonDeleteMultiple);
buttonDeleteMultiple.Click += new EventHandler(buttonDeleteMultiple_Click);
removeMultipleItemsToolStripMenuItem.DropDownItems.Add(buttonHost);
Button buttonCancelMultipleDelete = new Button();
buttonCancelMultipleDelete.Text = "CANCEL";
buttonHost = new ToolStripControlHost(buttonCancelMultipleDelete);
buttonCancelMultipleDelete.Click += new EventHandler(buttonCancelMultipleDelete_Click);
removeMultipleItemsToolStripMenuItem.DropDownItems.Add(buttonHost);
removeMultipleItemsToolStripMenuItem.DropDown.AutoClose = false;
menuVendorSection.AutoClose = false;
}
private void buttonDeleteMultiple_Click(object sender, EventArgs e)
{
//delete items
for (int dmi = 0; dmi < removeAllItemsToolStripMenuItem.DropDownItems.Count - 2; dmi++) //do not include buttons
{
((Detail)removeAllItemsToolStripMenuItem.DropDownItems[dmi].Tag).Delete(); //deletes item from database
}
//rebuild leaf
treePromotions.SelectedNode.Nodes.Clear();
addItemNodes(treePromotions.SelectedNode); //builds leaf nodes from database
//close menus
removeMultipleItemsToolStripMenuItem.DropDown.Close();
menuVendorSection.AutoClose = true;
menuVendorSection.Close();
}
private void buttonCancelMultipleDelete_Click(object sender, EventArgs e)
{
//just close menus
removeMultipleItemsToolStripMenuItem.DropDown.Close();
menuVendorSection.AutoClose = true;
menuVendorSection.Close();
}
If someone is still interested, here is a vb solution:
1) For the parent tool strip menu item, add the following handler in the form's constructor:
AddHandler ParentTSMI.DropDown.Closing, AddressOf onDropDownClosing
2) The handler:
Private Sub onDropDownClosing(sender As Object, e As ToolStripDropDownClosingEventArgs)
If e.CloseReason = ToolStripDropDownCloseReason.ItemClicked Then
e.Cancel = True
End If
End Sub
That's it all.
Don't forget to remove the handler (RemoveHandler) when you close the form.

How can I read a dynamically created textbox

I create some dynamic textbox's and a button in a placeholder and would like to save info in textbox's when button is clicked but not sure how to retrieve data from the textbox
LiteralControl spacediv3 = new LiteralControl("&nbsp&nbsp");
Label lblComText = new Label();
lblComTitle.Text = "Comment";
TextBox txtComment = new TextBox();
txtComment.Width = 200;
txtComment.TextMode = TextBoxMode.MultiLine;
phBlog.Controls.Add(lblComText);
phBlog.Controls.Add(spacediv3);
phBlog.Controls.Add(txtComment);
Button btnCommentSave = new Button();
btnCommentSave.ID = "mySavebtnComments" ;
btnCommentSave.Text = "Save ";
phBlog.Controls.Add(btnCommentSave);
btnCommentSave.CommandArgument = row["ID"].ToString();
btnCommentSave.Command += new CommandEventHandler(btnSave_Click);
protected void btnSave_Click(object sender, CommandEventArgs e)
{
firstelement.InnerText = txtComment.text // this gives error on txtComment.text
}
You need to get a reference to your control in btnSave_Click. Something like:
protected void btnSave_Click(object sender, CommandEventArgs e)
{
var btn = (Button)sender;
var container = btn.NamingContainer;
var txtBox = (TextBox)container.FindControl("txtComment");
firstelement.InnerText = txtBox.text // this gives error on txtComment.text
}
You also need to set the ID on txtComment and recreate any dynamically created controls at postback.
You will need some mechanism to create a relation between the Button and the TextBox obviously. In winforms this would be easy, where each control has a Tag property, which can contain a reference to pretty much anything. The web controls don't have such a property (that I know of), but it is still easy to maintain such relations. One approach would be to have Dictionary in the page storing button/textbox relations:
private Dictionary<Button, TextBox> _buttonTextBoxRelations = new Dictionary<Button, TextBox>();
When you create the button and textbox controls, you insert them in the dictionary:
TextBox txtComment = new TextBox();
// ...
Button btnCommentSave = new Button();
// ...
_buttonTextBoxRelations.Add(btnCommentSave, txtComment);
...and then you can look up the text box in the button's click event:
protected void btnSave_Click(object sender, CommandEventArgs e)
{
TextBox commentTextBox = _buttonTextBoxRelations[(Button)sender];
firstelement.InnerText = txtComment.text // this gives error on txtComment.text
}
Try during postback to load txtComment (with the same ID) in overridden LoadViewState method after calling base.LoadViewState. In this case you do load it before postback data are handled and txtComment control comes loaded.
Add an 'ID' to the textbox
txtComment.ID = "txtComment"
Request the information from the submitted form (provided you have a form on the page)
comment = Request.Form("txtComment")

Select text from multiple textboxes simultaneously

i have 10 textboxes and i need to select text from each one of them.
The problem is that i cant select text from multiple textboxes.
Is there any solution for this problem my code is.
private void Form1_Load(object sender, EventArgs e)
{
createTextBoxes(10);
((TextBox)textBoxes[0]).Select(1, 4);
((TextBox)textBoxes[1]).Select(1, 4); // <- it will not select text
((TextBox)textBoxes[2]).Select(1, 4); // same here
}
Control[] textBoxes;
private void createTextBoxes(int cnt)
{
textBoxes = new Control[cnt];
for (int i = 0; i < cnt; i++)
{
TextBox tb = new TextBox();
tb.Name = i.ToString();
tb.Location = new Point(5, 5 + 14 * i);
tb.Size = new Size(600, 20);
tb.BorderStyle = BorderStyle.None;
tb.Text = "sample text" + i.ToString();
textBoxes[i] = tb;
this.Controls.Add(tb);
}
}
Set the HideSelection property of the texboxes to false. They will maintain selection after losing focus.
Only one control can have a "Focus" at a time... you can't select (ie:highlight) text of multiple controls.
I also just tested by adding a button to the form and posted your 3 "select" snippets there too... nothing showed highlighted. However, when I did a TAB through each control, the first 3 respectfully showed the highlighted section. When I tabbed through the rest, the entire field of the rest of the textboxes were fully selected.
Or are you really trying to accomplish something else...
The text is selected you just can't see it cause of focus.
I ran your code and after doing so tabbed through the controls. The first 3 are selected as specified.
This is possibly not working because even though you've added the TextBox instances to the Form, they have not yet been displayed. Until they are displayed and initially rendered it's likely not possible to initiate a selection on them.
Actually it does, the problem is that the other 2 of your textboxes ([1] and [2]) don't have focus. Only one control can have focus at a time. If you hit tab to give focus to the next TextBox, you'll see the text selected.

Categories