I have a TabControl that starts with three TabPages in it. On the first tab there is a NumericUpDown (spinner) which displays the number of tabs and allows a user to add up to 10 extra tabs. Once they add more than about 5 or 6 it goes beyond the width of the form and the rest of the tabs are accessible by a couple of left/right arrows at the top. When going all the way to the right and then using the spinner to go back down to 0 (removing all the extra tabs and leave the starting three) it removes all tabs from the top of the pane and only by setting the spinner back to 1 does it refresh and display all 4 (3 from the start plus the 1 from the spinner).
I have tried several commbinations of
Application.DoEvents()
this.Refresh()
this.Invalidate()
this.Update()
but nothing seems to work. can anybody suggest a reason why it is not updating/refreshing?
public partial class Form1 : Form
{
TabPage[] tabs;
public Form1()
{
InitializeComponent();
tabs = new TabPage[tabControl1.Controls.Count];
tabs[0] = tabPage1;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
int numTabs = tabControl1.Controls.Count;
decimal spinnerValue = numericUpDown1.Value;
if (numTabs < spinnerValue) //add a tab
{
TabPage[] newTabs = new TabPage[(int)spinnerValue];
for (int i = 0; i < numTabs; i++)
{
newTabs[i] = tabs[i];
}
TabPage tab = new TabPage("Tab " + numTabs);
newTabs[(int)spinnerValue-1] = tab;
tabControl1.Controls.Add(tab);
tabs = newTabs;
}
else //remove a tab
{
TabPage[] newTabs = new TabPage[(int)spinnerValue];
for (int i = 0; i < spinnerValue; i++)
{
newTabs[i] = tabs[i];
}
tabControl1.Controls.Remove(tabs[(int)spinnerValue]);
tabs = newTabs;
}
}
}
Without seeing any code or knowing what type of project this is winforms, WPF, ASP.NET etc..
it's hard to give a definite answer, I am going to assume that this is WinForms
I'm not sure if you can. The following is a quote from MSDN:
"Controls contained in a TabPage are not created until the tab page is shown, and any data bindings in these controls are not activated until the tab page is shown."
However, instead of having the update code get the values from the controls directly, maybe you could create a class that could hold the Data you use to populate the controls and then when the update code is called it asks the class for the value and the class checks if the control is loaded and otherwise it gets the value from the Data instead.
Related
i have a small solution including big amount of data in #temp table and then make queries to produce around 16 reports
i have four tab controls each one include 5 reports
i use tabcontorl.hide () to control these four categories of reports for user usablity
the problem is i want to dock each tab control so it takes the size of the form
i tried to make a panel and put the tabcontrol inside and it works but for one tabcontrol when i put another on , just one appear and tried bringtofront() but no way.
i tried to create multiple panels but i can't put them over each other
any solution for this
Put 4 tab controls on your form, in design view, so they're laid out nicely and you can see them each taking up a quarter of the form (so you can still edit them):
1 2
3 4
Then in your form constructor, after the call to InitializeComponent, set each of the tabcontrols dock to Fill and Visible to false
public Form1(){
InitializeComponent();
tabControl1.Dock = tabControl2.Dock = tabControl3.Dock = tabControl4.Dock = DockStyle.Fill;
tabControl1.Visible = tabControl2.Visible = tabControl3.Visible = tabControl4.Visible = false;
}
Then switch between them by making them all Visible = false, and then only the one you want to Visible = true:
public void ShowTab2_Click(object sender, ClickEventArgs e){
tabControl1.Visible = tabControl2.Visible = tabControl3.Visible = tabControl4.Visible = false;
tabControl2.Visible = true;
}
Or however it is you're managing the user switching tab controls
I am new to C# and I use windows forms. I have Form1 with 20 buttons on it (button1 to button20).
How can I loop all those 20 buttons and change their text to for example "Hello" text?
Anyone knows how to achieve this? Thank you
Somewhere in your form's code behind:
foreach(var btnControl in this.Controls.OfType<Button>())
{
btnControl.Text = "Hello";
}
A simple loop will work find until you introduce containers into the page (groupboxes, tabs, etc). At that point you need a recursive function.
private void ChangeButtons(Control.ControlCollection controls)
{
for (int i = 0; i < controls.Count; i++)
{
// The control is a container so we need to look at this control's
// children to see if there are any buttons inside of it.
if (controls[i].HasChildren)
{
ChangeButtons(controls[i].Controls);
}
// Make sure the control is a button and if so disable it.
if (controls[i] is Button)
{
((Button)controls[i]).Text = "Hello";
}
}
}
You then call this function passing in the Form's control collection.
ChangeButtons(this.Controls);
I have a custom Tabs Control I created. It works as a coloured Label for the tab itself and as a Panel to hold the contents. My application reads UI parameters from config files. Take this line as an example from the controls config:
RTFBOX=(ID - rtf1) (BOUNDS - 0,0,100,100) (MULTILINE - enable) (FILE - email_rules.rtf)
This line tells the application to create an instance of my custom RichTextBoxPlus class and the important thing to take from this is that it is set up to read rich text from the FILE parameter. If I don't add this RichTextBoxPlus to another Control it shows it's rich text formatting absolutely fine.
I have another config that reads actions at runtime, this can be simple stuff like telling a Button created with the controls config that when it is clicked, it should fire off an email using content from a TextBox control. I have an action that pairs controls to each tab in the Tabs custom control. For example:
ADDTOTABS=(OBJECT - tabsControl1) (CONTROLS - panel1, panel2)
This finds tabsControl1 and adds panel1 to the 1st tab and panel2 to the 2nd tab. In this example, Panels are being added to each tab instead of individual controls as the Panels could hold multiple controls, handled at runtime through the ADDTOPANEL action.
ADDTOPANEL=(OBJECT - rtf1) (TARGET - panel1) (TRIGGER - onload)
So the rtf1 instance of RichTextBoxPlus is added to panel1 which is then added to the respective Panel of the Tabs control's 1st tab.
What I have found is rtf1 displays with rich text formatting absolutely fine if added to panel1 but not adding panel1 to Tabs.
The ADDTOTABS action executes this method:
private void TabContents_Action(Tabs tabpanel, string[] ctrls)
{
string[] tabs = tabpanel.GetTabNames();
for(int i = 0; i < tabs.Length; i++)
{
Control control = this.Controls.Find(ctrls[i], true).FirstOrDefault();
tabpanel.SetTaggedObject(control, tabs[i]);
}
tabpanel.SetTabActive(tabs[0]);
}
The SetTaggedObject method of the Tabs class finds the Panel control that corresponds with the tab name provided:
public void SetTaggedObject(Control ctrl, string tab)
{
Control container = this.Controls.Find(tab, false).FirstOrDefault();
container.Controls.Add(ctrl);
}
Doesn't seem to be anything untoward about this method.
The SetTabActive method of the Tabs class has a little more bulk. This handles changing the appearance of all tabs so that inactive tabs look different to the active tab. It is also hides and shows the panels for each tab based on whether the tab is active.
public void SetTabActive(string tab)
{
LabelPlus activeTab = this.tabs.Find(x => x.Name.Equals(tab));
List<LabelPlus> inactiveTabs = new List<LabelPlus>(this.tabs.FindAll(x => !x.Name.Equals(tab)));
activeTab.BackColor = this.ActiveColor;
activeTab.ForeColor = this.ActiveForeColor;
string panelName = tab.Remove(tab.Length - this.tabSuffix.Length);
Panel activeTabPanel = (Panel)this.Controls.Find(panelName, true).FirstOrDefault();
activeTabPanel.Bounds = new Rectangle(
new Point(this.tabStart, this.originalLocation.Y + this.TabTotalHeight), this.Size);
ControlCollection activeTabCtrls = activeTabPanel.Controls;
foreach(LabelPlus inactiveTab in inactiveTabs)
{
inactiveTab.BackColor = this.InactiveColor;
inactiveTab.ForeColor = this.InactiveForeColor;
string inactivePanelName = inactiveTab.Name.Remove(inactiveTab.Name.Length - this.tabSuffix.Length);
Panel inactiveTabPanel = (Panel)this.Controls.Find(inactivePanelName, true).FirstOrDefault();//
inactiveTabPanel.Bounds = new Rectangle(
new Point(this.tabStart, this.originalLocation.Y + this.TabTotalHeight), this.Size);
ControlCollection inactiveTabControls = inactiveTabPanel.Controls
foreach (Control ctrl in inactiveTabControls) { ctrl.Location = new Point(0, ctrl.Location.Y); ctrl.Hide(); }
}
foreach (Control ctrl in activeTabCtrls)
{ ctrl.Location = new Point(0, ctrl.Location.Y); ctrl.Show(); }
}
Not sure but I'd say the issue must be in this method. Any thoughts?
I'm currently working on a small side project as a way of getting used to Forms in Visual Studio 2012, as I usually only work with Console Applications. My current layout is designed to use tabs, and the user is to specify how many of the tabs they need for this application. They then fill out some information and it will be formatted and output to a file at a location specified by the user. On to the questions.
In order to stop duplicate tabs from existing, I'm using the following:
private void comboTabs_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboSkills.SelectedIndex == 0)
{
tabControl1.TabPages.Remove(tab8);
tabControl1.TabPages.Remove(tab7);
tabControl1.TabPages.Remove(tab6);
tabControl1.TabPages.Remove(tab5);
tabControl1.TabPages.Remove(tab4);
}
//repeat for Index 1, 2 and so on
}
There will always be a minimum of 3 tabs, so the first selection on the combo box removes tabs 4 through 8. The next selection does the same, but then adds tab4 back again. This goes on for the following selections. Is there any way I can do this more conveniently?
Second question, each tab has a series of text boxes and combo boxes that users are to select information from. The problem I'm having is that I need to identify how many tabs the user has selected and then only pull information from those tabs. I'm aware that I can get the number of tabs with:
int numberoftabs = tabControl1.TabCount;
But after that I can't seem to read the information from them. I'm intending to do
for (int i = 0; i < numberoftabs; i++)
{
//get textbox text of tab i and so on
}
Is there any way I can do this? I was hoping to use a tab layout since I like my current layout very much. If it makes a difference, all the tabs have the same layout, and share a naming convention such as tab 1 text box 1 is textTab1Name, tab 2 text box 2 is textTab2Name and so on.
For the first part of your question, you can handle all cases with this piece of code:
var tabCount = 5 - comboBox1.SelectedIndex;
for (var i = 0; i < tabCount; i++)
{
tabControl1.TabPages.RemoveAt(7-i);
}
For the second part you will have to create this method:
private T GetControl<T>(string name) where T : Control
{
return (T) this.Controls.Find(name, true).FirstOrDefault();
}
Then you can write your text retrieval loop like this:
for (int i = 0; i < numberoftabs; i++)
{
//get textbox text of tab i and so on
TextBox textBox1 = GetControl<TextBox>("textTab" + i + "Name");
...
etc..
}
I have a windows form application with a ComboBox on it and I have some strings in the box. I need to know how when I select one of the strings and press my create button, how can i make that name show up on another windows form application in the panel I created.
Here is the code for adding a customer
public partial class AddOrderForm : Form
{
private SalesForm parent;
public AddOrderForm(SalesForm s)
{
InitializeComponent();
parent = s;
Customer[] allCusts = parent.data.getAllCustomers();
for (int i = 0; i < allCusts.Length; i++)
{
Text = allCusts[i].getName();
newCustomerDropDown.Items.Add(Text);
newCustomerDropDown.Text = Text;
newCustomerDropDown.SelectedIndex = 0;
}
now when i click the create order button I want the information above to be labeled on my other windows form application.
private void newOrderButton_Click(object sender, EventArgs e)
{
//get the info from the text boxes
int Index = newCustomerDropDown.SelectedIndex;
Customer newCustomer = parent.data.getCustomerAtIndex(Index);
//make a new order that holds that info
Order brandSpankingNewOrder = new Order(newCustomer);
//add the order to the data manager
parent.data.addOrder(brandSpankingNewOrder);
//tell daddy to reload his orders
parent.loadOrders();
//close myself
this.Dispose();
}
The context is not very clear to me, but if I got it right, you open an instance of AddOrderForm from an instance of SalesForm, and when you click newOrderButton you want to update something on SalesForm with data from AddOrderForm.
If this is the case, there are many ways to obtain it, but maybe the one that requires the fewer changes to your code is this one (even if I don't like it too much).
Make the controls you need to modify in SalesForm public or at least internal (look at the Modifiers property in the Design section of the properties for the controls). This will allow you to write something like this (supposing customerTxt is a TextBox in SalesForm):
parent.customerTxt.Text = newCustomerDropDown.SelectedItem.Text;