Click here to Skip to main content
15,892,674 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a form with a button and a tabcontrol/tabpage component. The button is on the form, not in the tabcontrol. I'm trying to position the button in relation to the tabcontrol. I was told not to use the Controls.Add() method.

I'm using the Point class to determine the .X .Y of the tabcontrol. However, when I click on tabpage index 0, I set the .Left and .Top properties to 0 and its referencing the top left corner of the form, not the tabcontrol. I searched online trying to find a way to associate the button location in relation to the tabcontrol but I could not find a reference. Is using the Controls.Add() the only way to get the button to reference its position relationship to the tabcontrol?

Thanks for any help.

C#
private void tabControl1_Selected(object sender, TabControlEventArgs e)
{
    Point locPoint = this.Location;//referencing tabControl1???
    int locLeft = locPoint.X;
    int locTop = locPoint.Y;
    if (tabControl1.SelectedIndex == 0)
    {
        button1.Left = 0;// referencing form!
        button1.Top = 0;
    }
    if (tabControl1.SelectedIndex == 1)
    {
        button1.Left = locLeft;
        button1.Top = locTop;
    }
    if (tabControl1.SelectedIndex == 2)
    {
        button1.Left = locLeft;
        button1.Top = locTop;
    }
}
Posted
Comments
BillWoodruff 15-Sep-13 1:57am    
Please clarify: the position of the Button on the Form will be set once in relation to the position of the TabControl; or, what you want is that as the user selects different TabPages, the position of the Button moves in relationship to the TabPage selected ?

What is the reason you cannot use a Button on each TabPage ?
rfresh 15-Sep-13 11:29am    
I'm building an app as a prototype to test positioning of components on different tabpages. I will end up with several textboxes, several radio buttons and several listboxes, etc. Some of these components will be positioned on tabpage1 and others on tabpage2.

My plan is to make visible those components that will show on tabpage1 and hide the rest. Then show the others on tabpage2 and hide those shown on tabpage1. The reason I'm not just adding the target components to the target tabpage is because my tabpages will be dynamic and I won't know ahead of time how many there will be so I have to create the tabpages dynamically.

I'm trying to develop the understanding of how to position a component (I'm starting with just a button) in relation to a tabcontrol. If I can figure that out, then I can position it on any tabpage, at any position within that tabpage.

I was told by a c# developer not to put controls on tabpages directly, but to put them on the form and then move them onto the tabpage when you need them.

I hope I'm an clear on what I am trying to do.

Firstly to address the problem where the button is not being positioned in relation to the tabcontrol ...

C#
private void tabControl1_Selected(object sender, TabControlEventArgs e)
        {
            Point locPoint = this.tabControl1.Location;//referencing tabControl1???
            int locLeft = locPoint.X;
            int locTop = locPoint.Y;

            button1.Left = locLeft;
            button1.Top = locTop;

        }
will do it. You only need to look at the selectedIndex if you're using a different position per tab - but start simple and build up from there.
As it stands this code will obscure the tab so you can't select any more, so consider changing the .Top position (e.g. + 50)

Secondly - not sure why you were told to not put controls directly onto the tabpages - you can do this dynamically just as easily as adding them to the form - clickety[^] and you get all that pesky visibility depending on which tab selected for free
 
Share this answer
 
I'm building an app as a prototype to test positioning of components on different tabpages. I will end up with several textboxes, several radio buttons and several listboxes, etc. Some of these components will be positioned on tabpage1 and others on tabpage2.

My plan is to make visible those components that will show on tabpage1 and hide the rest. Then show the others on tabpage2 and hide those shown on tabpage1. The reason I'm not just adding the target components to the target tabpage is because my tabpages will be dynamic and I won't know ahead of time how many there will be so I have to create the tabpages dynamically.


Please consider that:

0. the very nature of how a TabControl works is that you see only the Controls on the selected TabPage, so I can't imagine why you would need to think about showing, or hiding, Controls in the way you've described. Each TabPage can have its own EventHandlers for many of the usual things every Control handles, like MouseDown, but TabPages cannot have their own individual handlers for Events like KeyPress.

0.a. it is possible to actually move Controls from one TabPage to another; it's a hack, and I've done it myself just an experiment, but I can't see any practical use for it, and it invites complications, imho.

1. a TabControl sited on a Form has Size, Location, BoundingBox, DisplayRectangle, and ClientRectangle, Properties that you can inspect, and those will all be in Form co-ordinate space.

2. within the TabControl, each TabPage will have the identical Size, Location, DisplayRectangle, etc., values: they are all located in the co-ordinate space of the TabControl.

2.a. you have some access to the structure of the Tab object that decorates the "virtual top" of each TabPage, which will be mentioned below.

3. you can resize the TabControl itself, and all the TabPages will resize, but, you can never change the size of an individual TabPage so it is different from that of any other TabPage.

3a. you could, through whatever means reduce the effective visible size of a TabPage by whatever means (black panels ?), but that's an odd idea, and probably will lead to a bizarre experience for the user: fugeddabouttit.

I'm trying to develop the understanding of how to position a component (I'm starting with just a button) in relation to a tabcontrol. If I can figure that out, then I can position it on any tabpage, at any position within that tabpage.

0. learning about positioning is a good thing. among the important things you'll want to focus on are:

a. understanding how to map points and rectangles from a Form's co-ordinate system to the Screen's co-ordinate system, and to a Control's co-ordinate system, and the reverse, is a very good thing. you need to master these:

a.1. PointToScreen, PointToClient
a.2. RectangleToScreen, RectangleToClient

1. Here you need to clarify to me, or distinguish for yourself, the difference between positioning a Control which is in the ControlsCollection of a Form, and a Control which is in the ControlCollection of a TabPage.

2. If you want to make some Control, in a Form, be on top of a TabControl and TabPage: yes, you could do that, and, yes, you can determine the relative location of each TabPage as it is selected by using the GetTabRect(SelectedIndex) method of the TabControl (look it up), and move a Control on a Form around based on which TabPage is selected.

3. But, having a Control on the Form on top-of a TabControl, or moving around on the Form to align itself with which TabPage is selected as the selected TabPage changes is: a really "crazy" design idea :) I can show you code for how to do that, but I seriously advise you not to implement this type of Control relationship unless there is some compelling "organic" reason to do so (I can't imagine one, but, who knows ?).

I was told by a c# developer not to put controls on tabpages directly, but to put them on the form and then move them onto the tabpage when you need them.

0. I can't guess what the intent of the advice given to you was, but I see every reason for you to put the Controls on each TabPage necessary for each TabPage, and I can't see any reason why they should be on the Form first, and then added at design-time to the TabPages. If every TabPage needs the same bunch of Controls, just set them at design-time on one TabPage, and get them looking like you want them, and then copy them, and paste them, into all the other TabPages: worst case is you'll have to move the pasted in Controls as a group to get them aligned where you want them thanks to Visual Studio having its own ideas about where to paste things.

1. perhaps your advisor was thinking of the situation where, at run-time you need to created new Controls on certain TabPages, on one TabPage, or on all TabPages ? That is possible.

~

Let's consider the issues in adding a Control to a TabPage at run-time:

0. you know every TabPage will have the same co-ordinate space, with 0,0 at upper-left.

1. you know that every Control already on the TabPage has position, size, etc. Properties you can access to know exactly where it is in TabPage co-ordinate space.

2. as an example, let's consider that you want to place a new Button on every TabPage at run-time, and you wanted it to be at a different y position on each TabPage based on some variable you defined, but at a fixed x location.

private Button btnToBeAdded;

private int btnXLocation = 30;
private int btnStartYLocation = 30;
private int btnYOffset = 40;

private void addButtonsToTabPages_Click(object sender, EventArgs e)
{
    for (int i = 0; i < tabControl1.TabPages.Count; i++)
    {
        // create the Button
        btnToBeAdded = new Button();

        // set visual attributes of the Button
        btnToBeAdded.BackColor = Color.AliceBlue;

        // name it ?, set it's Text
        btnToBeAdded.Name = "addedBtn" + i.ToString();
        btnToBeAdded.Text = "click Me";

        // give the button a tag
        btnToBeAdded.Tag = i;

        // add the Button to the TabPage
        tabControl1.TabPages[i].Controls.Add(btnToBeAdded);

        // set its location
        btnToBeAdded.Location = new Point(btnXLocation, btnStartYLocation + (btnYOffset * i));

        // give it a Click EventHandler
        btnToBeAdded.Click += btnToBeAdded_Click;
    } 
}

private void btnToBeAdded_Click(object sender, EventArgs e)
{
    Button btnClicked = sender as Button;

    // put some code here to be executed when the Button
    // on every TabPage is clicked ?
    // ...

    // make sure you handle every possible index value
    switch (Convert.ToInt32(btnClicked.Tag))
    {
        case 0:
            // call your code if added Button on first TabPage is clicked
            firstTabPageButtonDoSomething();
            break;
        case 1:
            break;
        case 2:
            break;
        // case #n:
            //break;
        default:
            // you have a problem
            throw new IndexOutOfRangeException();
    }
}

private void firstTabPageButtonDoSomething()
{
    MessageBox.Show("Button on first TabPage clicked");
}
Discussion: note how the Tag Property of each Button added to each TabPage was set to a numeric value that identified the index of the TabPage in the TabControl.

Then, all Buttons added can share the same 'Click EventHandler: in that EventHandler by using the Tag Property ... had to convert it to an integer because a Tag Property is of type Object ... you can then trigger execution of whatever you want each Button to do.
 
Share this answer
 
v4
Comments
rfresh 15-Sep-13 14:11pm    
Thank you Bill for that information. It will take me some time to digest it. In my backup utility app, I will have one tabpage with textboxes and checkboxes, etc. that will describe how the backup will be done (one way to destination, two-way syncing or from FTP server down to PC) in addition to having a treeview which says what folders/files are to be backed up. Thus, I may end up with one tabpage configured to back up My Documents as a one-way type, another tabpage configured to back up my Excel folder and sync both ways, etc. So the tabpages will display the exact same components, just set to different values. I'm using a List<syncjob> for my class to manage multiple tabpages. When I change tabpages, I will use the List<> and save the current tabpage values and then load in the values for the newly selected tabpage. So, I find myself now here at this point beause I am trying to 'move' the components from one tabpage to another as I click on a different tabpage. The components are the same, I just need to move them and reload in their values. Someone advised me not to put the components on the tabcontrol at design time. I moved them off onto the form but couldn't get them to postion correctly until CHill60 told me the solution to that problem: Point locPoint = this.tabControl1.Location; So that did work and then you Bill told me to consider using a TableLayoutPanel. I like that and I'm going to spend some time building a prototype to see how that might work. I think it is a good suggestion because then all I have to do is move one component onto each tabpage as I click.
The OP finally removed the seventh veil: " ... one tabpage with textboxes and checkboxes ... will describe how the backup will be done (one way to destination, two-way syncing or from FTP server down to PC) ... having a treeview which says what folders/files are to be backed up. ... one tabpage configured to back up My Documents as a one-way type, another tabpage configured to back up my Excel folder and sync both ways, etc. ... tabpages will display the exact same components, just set to different values.

I'm using a List for my class to manage multiple tabpages. When I change tabpages, I will use the List<> and save the current tabpage values and then load in the values for the newly selected tabpage. ... trying to 'move' the components from one tabpage to another as I click on a different tabpage. The components are the same, I just need to move them and reload in their values."


Well, now I finally understand what your design issue is :)

Yes, if you have identical controls in a number of possible scenarios that only change their apparent value, what the user sees, or only change what they do in your code when the controls are used, or change trivial things, like what the Button Text is: you do want to have only one set of Controls, not duplicate the Controls on each TabPage.

So, in this case: the only real use you are making of the TabControl is to provide a way to change the aspects of the view the user sees, depending on their current "task," and, possibly, change, in your code, what you do based on which current view the user is interacting with.

There are other ways you could handle this without using a TabControl, but if you really must use a TabControl, then think about this:

Note: comments and code that follow address only the issue of managing the design challenge using a TabControl: I do not address the issues of how the OP will "save state" of the Controls on the current TabPage before they switch to another TabPage, and how they will restore the "state" of the Controls when the user switches back to the previous TabPage.

1. put 1 panel on the first TabPage: put all the Controls every TabPage will use into that Panel. get their appearance and position set as you wish.

2. use the TabControl's Selected Event to keep track of the current selected TabPage:

2.a. if the previously selected TabPage is not null, then remove panel1 from the previous TabPage's ControlCollection

2.b. add panel1 to the currently selected TabPages ControlCollection
C#
private TabPage currentTabPage;

private void tabControl1_Selected(object sender, TabControlEventArgs e)
{
    if (! (currentTabPage == null)) 
    {
        // here's where you write the code to save
        // whatever information about the previously selected
        // TabPage you want to save
        // ... save whatever ...

        currentTabPage.Controls.Remove(panel1);
    }

    currentTabPage = e.TabPage;

    // see Discussion below
    tabControl1.SuspendLayout();
    currentTabPage.SuspendLayout();
    panel1.SuspendLayout();
    this.SuspendLayout();

    // here's where you write code to configure
    //  Controls in panel1 based on whatever you saved previously
    // ... configure Controls on panel1 to whatever ...

    // for educational purposes only
    Console.WriteLine("selected tabPage: " + currentTabPage.Name);
    Console.WriteLine("selected tabPage's Tab rectangle: "
        + tabControl1.GetTabRect(e.TabPageIndex));
    Console.WriteLine();

    currentTabPage.Controls.Add(panel1);
    // probably unnecessary, but why not ?
    panel1.Dock = DockStyle.Fill;

    // see Discussion below
    tabControl1.ResumeLayout(false);
    tabPage1.ResumeLayout(false);
    panel1.ResumeLayout(false);
    panel1.PerformLayout();
    this.ResumeLayout(false);
    this.PerformLayout();
}
Discussion:

0. what's all that funky stuff you are doing with Layout ?

well, that's exactly the way the Windows Form Designer.cs files handle Layout, and what you see may be "overkill" but I am putting that in so (hopefully) this reply has more educational value on CP.

The fact is that even with the Form's DoubleBuffered property set to 'true, and using the above code, you will see a slight "jump" as you switch from TabPage to TabPage at run-time. Be sure and write MS about that, if it bothers you :)

1. how about a hint on how to manage the issue of saving, and restoring, the "state" of the one set of Controls that are now automatically "moved into" the current selected TabPage ?

1.a. depends on what you need to save: do you, for example, want to save the entire state of the TreeView file-browser: that's unlikely because the folders and files that were valid when the user last opened his C/.../.../.../WhatEverFolder may have changed. So, in that case you'll just need to call the code that takes a saved folder path (string), and reload the TreeView.

1.b. whatever data-structure you do create to hold any saved data for re-use: the "key" to it will be obviously the index of the TabPage. look at the use of setting the Tab property in each control added in my first solution on this thread, and, later, using that Tab property to take differential action in a switch/case statement.

1.c. my assumption is that you'd never want to have such a complex (nested) set of Controls (Panels within Panels that hold Controls, etc.) that you'd need to recursively parse the entire contents of the outer Panel: in fact, if I saw something like that, I'd immediately think it was bad design. so, do whatever you can at design-time to "mark" (by setting the Control's Tag property) Controls you will need to save state from, and store them in a List<Control>.

1.d. another possible design is to define your own Controls that inherit from the WinForms Controls, and extra fields, and properties, or serialization facilities, etc., to use in saving and restoring.
 
Share this answer
 
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900