Some inits done earlier in the code...
private List<System.Windows.Forms.TabPage> tab_pages = new List<System.Windows.Forms.TabPage>();
int tab_increment = 0;
Somewhere in the code, I create a bunch of tab pages in real-time.
for (i=0; i<5; i++)
{
tab_pages.Add( new System.Windows.Forms.TabPage() );
tab_pages[tab_increment].Location = new System.Drawing.Point(4, 22);
tab_pages[tab_increment].Name = 1 + tab_increment.ToString();
tab_pages[tab_increment].Size = new System.Drawing.Size(501, 281);
tab_pages[tab_increment].Text = tab_increment.ToString();
this.tabControl.Controls.Add(tab_pages[tab_increment]);
tab_increment += 1;
}
Now I would like to access elements that are these tab pages. Also let's pretend that I created different elements on each page (example, tabPage[0] a button, tabPage[1] a checkbox, etc), how do I access them knowing that everything was added dynamically?
Check this approach:
void Walk(Control control)
{
foreach (Control c in control.Controls)
{
//just walking through controls...
//...do something
//but remember, it could contain containers itself (say, groupbox or panel, etc.)...so, do a recursion
if (c.Controls.Count > 0)
Walk(c);
}
//or
foreach (Button btn in control.Controls.OfType<Button>())
{
//an example of how to walk through controls sub array of certain type
//this loop won't have a single iteration if this page contains no Buttons
//..so you can replace Button
//and have some certain code for different types of controls
}
}
And launch it for tabcontrol:
foreach (TabPage page in tabControl1.TabPages)
Walk(page);
I guess there is no special need to have separate collection of tabpages for one tabcontrol, as soon as it has TabPages property.
In the code above I used Enumerable.OfType Method to get a subcollection of controls of certain type.
As for your code, try this:
for (int i = 0; i < 5; i++)
{
this.tabControl.Controls.Add(new System.Windows.Forms.TabPage());
this.tabControl.TabPages[i].Text = i.ToString();
//...do whatever you need
//...
//besdies, I think, ther's no need in tab_increment...loop index works well enough
}
In order to add pages, I think that using
tabControl.TabPages.Add(new TabPage("Name"));
or in your case
this.tabControl.TabPages.Add(tab_pages[tab_increment]);
is more suitable.
In order to access them you could use
TabPage tp = tabControl.TabPages[i]; //where i is the index of your TabPage
and you can use TabPage.Controls.Add of the Controls property to add any Control on the TabPage like:
Button btn = new Button();
btn.Name = "Button name";
tp.Controls.Add(btn);
You can use the Controls property on the TabPage object. Each control in the collection is given to you as a Control, and it is up to you to cast them to the type that you want.
Related
Has C# indexed control arrays or not? I would like to put a "button array" for example with 5 buttons which use just one event handler which handles the index of all this 5 controls (like VB6 does). Else I have to write for each of these 5 buttons one extra event handler. And if I have 100 buttons, I need 100 event handlers? I mean something like that:
TextBox1[i].Text="Example";
It could make coding definitely easier for me to work with control arrays. Now I have seen, that C# at least has no visible array functionality on user controls and no "index" property on the user controls. So I guess C# has no control arrays, or I must each element call by known name.
Instead of giving 100 TextBoxes in a for loop 100 incrementing values, I have to write:
TextBox1.Text = Value1;
TextBox2.Text = Value2;
...
...
TextBox100.Text = Value100;
A lot of more work + all these 100 event handlers each for one additional TextBox extra.
I know I'm a little late to this party, but this solution will work:
Make a global array:
TextBox[] myTextBox;
Then in your object's constructor, after the call to
InitializeComponent();
initialize your array:
myTextBox = new TextBox[] {TextBox1, TextBox2, ... };
Now you can iterate your array of controls:
for(int i = 0; i < myTextBox.Length; i++)
myTextBox[i].Text = "OMG IT WORKS!!!";
I hope this helps!
Pete
As I mentioned in comment to a solution by HatSoft, C# Winforms does not allow you to create control arrays like old VB6 allowed us. The nearest I think we can get to is what HatSoft and Bert Evans in their posts have shown.
One thing that I hope would satisfy your requirement is the event handler, you get a common event handler and in the event handler when you typecast the "sender" you get the control directly just like you would in VB6
C#
TextBox textBox = sender as TextBox;
VB6
TextBox textBox = TextBox1[i];
So the only trouble you might have is wiring those 100 TextBoxes to a single event handler, if you are not creating the controls dynamically through code rather creating it manually at design time then all one can suggest is group them in a container like say Panel. Then on Form Load wire them all up to a single event handler like this:
foreach (Control control in myTextBoxPanel.Controls)
{
if(control is TextBox)
control.TextChanged += new EventHandler(control_TextChanged);
}
Just create one handler and point all the buttons to it.
var ButtonHandler = (sender, args) => {
var clicked = (Button)sender;
if (clicked.Text == "whatever")
//do stuff
else
//do other stuff
};
button1.Click += ButtonHandler;
button2.Click += ButtonHandler;
Alternatively, if you are creating controls in code, you could use one of the techniques specified in this answer.
Instead of giving 100 TextBoxes in a for loop 100 incrementing values, I have to write:
for(int i = 0; i <100; i++)
{
TextBox t = new TextBox(){ Id = "txt_" + i, Value = "txt_" + i};
t.TextChanged += new System.EventHandler(this.textBox_Textchanged);
Page.Controls.Add(t);
}
//and for event on TextChanged
private void textBox_Textchanged(object sender, EventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
////
}
}
Another thing to note: if you really need to edit 100 strings on one form, you should probably think about whether 100 text boxes is really the best way to do it. Perhaps a ListView, DataGridView, or PropertyGrid would be better suited.
This applies almost any time you think you need a huge array of controls.
If you are working with Web Forms and not MVC, you can acces a collection of controls on the page as shown in Using the Controls Collection in an ASP.NET Web Page. Essentially the controls collection is a tree with the page hosting the first level of child controls and some items having children of their own. See How to: Locate the Web Forms Controls on a Page by Walking the Controls Collection for an example of how to follow the tree.
Also, see How to: Add Controls to an ASP.NET Web Page Programmatically.
You can use the same event handler for multiple items as long as the signature required is the same.
For Windows Forms this is nearly identical since they're based on similar architectural models, but you'll want Control.Controls Property and How to: Add Controls to Windows Forms.
Keeping it simple:
TextBox[] keybox = new TextBox[16]; //create an array
for (int i=0; i<16; i++)
{
keybox[i] = new TextBox(); //initialize (create storage for elements)
keybox[i].Tag = i; //Tag prop = index (not available at design time)
keybox[i].KeyDown += keybox_down; //define event handler for array
}
private void keybox_down(object sender, KeyEventArgs e)
{
int index = (int)((TextBox)sender).Tag //get index of element that fired event
...
}
I'm still new at coding. I'm making a calculator but I also want a lot of other things in it. like a conversion calculator, cook book, and kanji radical dictionary in c# WindowsFormsApplication I want to change from one to the next using a comboBox so I was going to make a array with all the control I wish to hide/show
string[] numList = {"button0","button1","button2", "button3"};//this will have all number and .
for (int i = 0; i < numList.Length; i++)
{
numList[i].Hide();
}
But it tell me there no definition for 'Hide' but when I switch numList[i] to button0 it work but I don't wish to wright the same 11 control for every time i add something to the comboBox anyway to fixes this or any other method
If you want to hide all Buttons then try this:
foreach (Button control in Controls.OfType<Button>())
{
(control).Hide();
}
This iterate through all Buttons of the form and hide them. But if you want to just hide a specific buttons then you can set the Tag property of that buttons to something like OP then to hide only that Buttons:
foreach (Button control in Controls.OfType<Button>())
{
if (control.Tag.ToString() == "OP")
{
(control).Hide();
}
}
Or with linq:
foreach (Button control in Controls.OfType<Button>().Where(control => control.Tag.ToString() == "OP"))
{
(control).Hide();
}
try below code
private void btnHide_Click(object sender, EventArgs e)
{
string[] buttonList = { "button1", "button2", "button3" };
for (int i = 0; i < buttonList.Length; i++)
{
Control[] ctrl = this.Controls.Find(buttonList[i], true);
((Button)ctrl[0]).Visible = false;
}
}
You are keeping a list of strings, you should actually add the buttons in to the list in order to have the Hide method visible
Control[] numList = {button0, button1, button2, button3 };
I have a windows form that just exists to take input from user, for all intents and purposes it is just a label and a corresponding input box (textbox, checkbox, masket textbox etc).
I have programatically placed all the input fields in a TabIndex order that is optimal for cycling through them in a way that fits with their locations (tab down each column of inputs, then to the top of the next column).
The person that I am building this project for has stipulated that only like each textbox to come available one at a time, as the previous one has been filled out. This is a crude way of doing it with no validation, but essentially something like this...
if (String.IsNullOrEmpty(textbox1.Text))
{
textbox2.Enabled = true
}
So this is fine to do with two textboxes in this example, but the form has 28 different inputs on it, so an absurd series of if statements will only be a last resort.
My thoughts has been to put all the inputs in a List, ideally in the same order as is their TabIndexes. I tried to do this using a foreach loop...
List<Control> inputsList = new List<Control>();
public void initialiseControls()
{
//control position to insert control into list at specified index
int cntrlpos = 0;
//for every control in form
foreach (Control cntrl in this.Controls)
{
//not counting labels (not input fields)
if (!(cntrl is Label))
{
//set list position to equal control's TabIndex
cntrlpos = cntrl.TabIndex;
//insert the control into the list at the position reflecting TabIndex
inputsList.Insert(cntrlpos, cntrl); //<---- Error Occurs
//ASK TEXTBOX TO OUTPUT LIST POSITION AS TEST
//foreach (var txtbx in this.Controls.OfType<TextBox>())
//{
// txtbx.Text = Convert.ToString(cntrlpos);
//}
}
}
As soon as the function is called, an exception is thrown stating that "Index must be within the bounds of the list".
When I put a breakpoint into the code, it showed cntrlpos to equal 29, which is more than the 28 total input controls there are on the form.
I don't know where to go from here, if anyone can offer some advice on the code above to place the Controls into the list in the correct order (or point me in the direction of another method to do something like this), then I would really appreciate it.
Thanks,
Mark
To make your list, try this:
List<Control> inputList =
(from Control c in getAllControls(this)
where c.TabStop
orderby c.TabIndex
select c).ToList();
Define the method getAllControls elsewhere in your form class:
IEnumerable<Control> getAllControls(Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control descendant in getAllControls(control))
yield return descendant;
}
}
(Taken and modified slightly from Recursive control search with Linq)
This will make it so that you get even nested controls (such as those in panels or groupboxes).
You can't just use the TabIndex as an index into your list, because even stuff like labels have tab indices, which will mess up your indices.
I think you've over complicated it...
Just use Control.GetNextControl:
Retrieves the next control forward or back in the tab order of child
controls.
For example, with just TextBoxes:
private void textBoxes_TextChanged(object sender, EventArgs e)
{
Control ctl = (Control)sender;
if (!String.IsNullOrEmpty(ctl.Text))
{
Control next = this.GetNextControl(ctl, true);
if (next != null)
{
next.Enabled = true;
}
}
}
Obviously you might need a slightly more complicated check for some other types of controls in a different handler, but you'd still just grab the next control to enable using GetNextControl().
I have a form that has TabControl with Dynamic TabPages in it. Each pages has usercontrol added using a loop. This is how I add the usercontrol in each pages.
for (var i = 0; i < tbl.Rows.Count; i++)
{
uctrTab = new XtraTabPagesUserCtrl();
xtab.TabPages[i].Text = "Table " + (i+1);
uctrTab.LayoutClicked += new MouseEventHandler(Layout_Click);
xtab.TabPages[i].Controls.Add(uctrTab);
xtab.TabPages[i].PageVisible = !xtab.TabPages[i].PageVisible;
}
The usercontrol I made has a DataGridView in it so i want to refresh the content of it but I dont know how to do that without removing and readding the control back.
right now my solution is
xtab.SelectedTabPage.Controls.Clear();
uctrTab = new XtraTabPagesUserCtrl();
uctrTab.LayoutClicked += new MouseEventHandler(Layout_Click);
xtab.SelectedTabPage.Controls.Add(uctrTab);
is there any better way of refreshing the content without having to do that?
I'm going to write my answer here (I did it in the comments section because question was on hold).
My suggestion.. first implement a method in your user control like..
public void RefreshGrid()
{
refresh datagridview data here
}
Second, itearate over selected tab page's controls and look for your usercontrol.. something like this..
foreach(Control ctrl in selectedTabPage.Controls)
{
if(ctrl is XtraTabPagesUserCtrl)
{
((XtraTabPagesUserCtrl)ctrl).RefreshGrid();
}
}
I have an UpdatePanel and in it a regular Panel. In the Panel I dynamically add simple UserControls. The Usercontrol has a Button and a Label. When I click on a button in a control it removes all controls in the Panel which I have added dynamically.
Can anyone help?
int controlID = 0;
List<Control> cc = new List<Control>();
if (Session["ControlsCompleted"] != null)
{
cc = Session["ControlsCompleted"] as List<Control>;
for (int i = 0; i < cc.Count; i++)
{
pnlCompletedEducation.Controls.Add(cc[i]);
}
controlID = cc.Count;
}
Controls_TestWebUserControl ct = LoadControl(#"Controls\TestWebUserControl.ascx") as Controls_TestWebUserControl;
ct.ID = controlID.ToString();
cc.Add(ct);
ct.EnableViewState = true;
pnlCompletedEducation.Controls.Add(ct);
txtInstitutionName.Text = controlID.ToString();
List<Control> lc = new List<Control>();
for (int i = 0; i < pnlCompletedEducation.Controls.Count; i++)
{
lc.Add(pnlCompletedEducation.Controls[i]);
}
Session["ControlsCompleted"] = lc;
This is how I add the controls to the panel. I had to keep them somewhere, and i couldn't do it with the ViewState, so i used a Session, which is a bad idea.
You say that you are adding the user control dynamically. Are you having code like this:
void Page_Load(...)
{
if (!IsPostback)
// AddUserControl here.
}
You need to add the user control during every request, also postbacks, because it will not be stored in the view state that you have modified the control tree.
You problem that you have not recreated (for example at Page_Load) dynamically added control.
Make sure that control is recreated on IsPostBack