0

I am trying to write a generic method which would allow me to add a form as the contents of a Tab on a tab control, however I am only able to figure out how to add it when specifying the exact form I wish to add.

Suppose I have three forms Form1, Form2, and Form3 where Form1 contains the tab control tabControl1, and Form2 inherits Form, where Form3 inherits MetroForm. Following is the method I am currently using :

private void AddFormAsTab() {
    Form3 f = new Form3()
    {
        TopLevel = false,
        ShowInTaskbar = false,
        ControlBox = false,
        SizeGripStyle = SizeGripStyle.Hide,
        Visible = false,
        Text = string.Empty,
        FormBorderStyle = FormBorderStyle.None,
        Dock = DockStyle.Fill,
        MinimizeBox = false,
        MaximizeBox = false,
        ShadowType = MetroFormShadowType.None,
        Movable = false
    };

    TabPage tab = new TabPage();
    tab.Controls.Add( f );
    tabControl1.TabPages.Add( tab );
    f.Visible = true;
}

What I would like to do, is modify this method so that AddFormAsTab() accepts any form type and sets the properties as necessary (to ensure they are set as I am lazy and do not wish to change all those properties on every form I design perpetually when recycling this component)

I have seen this done in other controls such as Telerik, and DevExpress where the control accepts a generic and modifies it to 'fit' the purpose. In my case, I am changing the properties of the form so that it fills the tabpage, has no border, title bar, etc.

I have considered using (typeof(T))obj type code, but this generates pre-compile errors where those properties do not exist in obj which prevent it from being built, even though in theory, this should work.

I have also tried stuff like Form f = new Form1(), however this doesn't work as Form is not the same type as Form1 which happens to inherit Form.

What can I do to make this happen, to allow any form to be set without hardcoding those specific forms classes into the AddFormAsTab() method, but still delegate responsibility to set the required properties to that method ?

Ideally, something like :

private void AddFormAsTab<T>(T obj) { ... }
Kraang Prime
  • 9,981
  • 10
  • 58
  • 124

1 Answers1

2

You can use either of these options:

public void AddFormAsTab<T>() where T : Form, new()
{
    var f = new T();
    f.TopLevel = false;
    //...
}

Usage:

AddFormAsTab<Form1>();

Or

public void AddFormAsTab(Form f)
{
    f.TopLevel = false;
    //...
}

Usage:

AddFormAsTab(new Form1());
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Looks good. I can't test this just yet as I am uninstalling VS2013 (tested VS2015 long enough to be ready to ditch 2k13) so while that is uninstalling I can't play so much. Can this form of method be overloaded ? Like `Form3` in my example is of the `MetroForm` base class. So could I do two of these methods of the same name (`AddFormAsTab`) just one says `where T : MetroForm, new()` and the other `where T : Form, new()` – Kraang Prime Aug 22 '16 at 07:32
  • Also, the problem with the `Form f` is it is not the same type as `Form1`, so Likely will use the first method as it looks like it takes into account the actual form as well as the inherited type instead of just assuming it is generic `Form` :) -- I have had problems with `Form f = new Form1()` code in the past. – Kraang Prime Aug 22 '16 at 07:34
  • *1)* You can simply use this signature: `public void AddFormAsTab() where T : MetroForm, new()` but you can not both `where T : MetroForm` and `where T : Form` and you don't need, because `MetroForm` derives from `Form` so I believe `here T : Form` is enough. – Reza Aghaei Aug 22 '16 at 09:14
  • *2)* `public void AddFormAsTab(Form f) Is OK`, because both `Form1` and `Form2` are `Form` and based on [Polymorphism](https://msdn.microsoft.com/en-us/library/ms173152.aspx) rules you can pass any class which derived from `Form` to the method. In your case you may prefer public void `AddFormAsTab(MetroForm f)` overload, while I think `public void AddFormAsTab(Form f)` is enough. – Reza Aghaei Aug 22 '16 at 09:14
  • Using the `T` method, could you show me how I would dispose of the form when the tab is disposed safely please ? I was considering inline event handler but that would make it so I am unable to unsubscribe from those events (memory leak). – Kraang Prime Aug 22 '16 at 11:07
  • Since you added forms as child controls and they are present in `Controls` collection of your tabpages, You don't need to dispose forms manually. But if you created a method to remove a tab page from `TabPages` collection, you need to `Close` your form. – Reza Aghaei Aug 22 '16 at 11:17
  • A little bit confused. I understand if I close the main form containing the tab control, all objects get their `Dispose()` method called, however when a Tab is removed (aka, tabControl1.TabPages.Remove(1)` am I not required to call `Dispose()` on the form or objects that were on that tab, and if so, is it as simple as `var tp = ...TabPages[1]; foreach(var control in tp.Controls { control.Dispose(); } .... TabPages[1].Dispose();` ? – Kraang Prime Aug 22 '16 at 11:28
  • *if I close the main form containing the tab control, all objects get their Dispose() method called* →Yes, it works this way. So you don't need to dispose child controls manually. But when you want to remove tab, first kepp a reference to the tab, then remove it, then cast the first control of tab to `Form` and call it's `Close` method. – Reza Aghaei Aug 22 '16 at 11:35
  • 1
    By the way, since you are creating a Tabbed UI, you may find this post useful [TabControl with Close and Add Button](http://stackoverflow.com/a/36900582/3110834). But pay attention, you should handle closing the form yourself. – Reza Aghaei Aug 22 '16 at 11:42