views:

271

answers:

3

I have a Form and a UserControl. The UserControl has a menu, and the form has a tabstrip (General, Food, Vitamins etc).

In the UserControl, I have the following code: (Form name is frmForm, the tab names in the form are tabGeneral,tabFood, tabVitamins)

  frmForm fm=new frmForm();
  fm.tabMain.Selected=tabVitamins;

I call these line from the UserControl to capture the tab to get selected on the form, but it does not select the vitamins tab.

Where am I going wrong? I have access specifier as Protected Internal for tabs in the form.

Please advice.

Thanks, Karthick

+1  A: 

You should not be creating a new frmForm() inside the user control. You could pass a reference to the frmForm to the user control.

In your user control constructor try something like this.

private frmForm fm;

public YourUserControl(frmForm fm)
{
    this.fm = fm;
}

Then you could use.

fm.tabMain.Selected=tabVitamins;

Does that help?

wes
This will prevent the control from being used in the designer.
SLaks
I just tried this in VS 2008 and it seems fine. Why do you think it will prevent the control from being used in the designer?
wes
The designer requires that the control have a public no-argument constructor.
SLaks
Have you tried this? I have 1 and only 1 constructor in my user control. It takes 1 argument of the frmForm. I can view the designer and run the app without a problem.
wes
Thanks wes for your inputs.
Karthick
I tried it, and it didn't work. What code is in your `.Designer.cs`?
SLaks
@SLaks : a reasonable hypothesis : that modifying the Designer.cs file to use an overloaded ctor for a UserControl : would screw things up, but, in fact, you can get away with it. @Wes : while you can do this, it is generally a bad idea to modify anything in the Designer.cs file, imho particularly not a good idea for newcomers to .NET. Other comments in my answer below. best,
BillW
You can get away with it until you change the form in the designer.
SLaks
+2  A: 

When you write new frmForm(), you're creating a completely new instance of frmForm, which is then discarded.

To get the frmForm instance that holds your control, call the FindForm() method and cast to frmForm.

For example:

frmForm myForm = FindForm() as frmForm; 
if(myForm != null)
    myForm.tabMain.SelectedTab = myForm.tabVitamins;

If the control is on some other form, this code won't do anything.


By the way, Hungarian notation is frowned upon in .Net.
Your form should probably be named something like MainForm.

SLaks
Thanks SLaks. Perfect, i changed myForm.tabMain.SelectedTab = frmForm.tabVitamins; to myForm.tabMain.SelectedTab = myForm.tabVitamins;
Karthick
Yes, you're right. I fixed that.
SLaks
+1  A: 

SLaks has correctly pointed out your fundamental error, and given you a valid example of a way, via a call to the method 'FindForm, to get the Form the UserControl is sited on.

It may be valuable to you to keep in mind that a UserControl (and all Controls) also has a 'Parent property, but, of course, a UserControl could be placed inside another Control on a Form (like your UserControl could be inside a Panel on the Form) : in that case the UserControl's Parent would be the control it's inside on the Form (like, a Panel), not the Form itself, but 'FindForm will do the right thing to get you the Form it's on.

However you are calling a Method every time you use 'FindForm, and "best practice" suggests that what you want to do is to "inject" a reference to the Form into the UserControl at run-time so that it can always access its Form property easily, without calling a 'Method.

In your example, on a practical level, this (calling the Method) may make almost no difference in performance, but, imho, as you get to a place with WinForms and .NET where you might have a UserControl that will need access to its Parent Form very frequently, this will pay off, and it's a better way to structure your code in the long run, for maintenance.

Wes showed you one way you can "embed" (inject) the UserControl's hosting Form : using an overloaded constructor for the UserControl. But that requires you to modify the Designer.cs file in standard WinForms, and I strongly advise you against that, even though it will work. Particularly if you are just "getting your feet on the ground" in .NET, I strongly advise you against modifying it, or anything having to do with the Form's constructor and its internal call to : InitializeComponent();

Also, as you progress with WinForms you are going to meet many situations where you are going to want instances of "objects" (a Control, a Form, an instance of a Class) to contain references to other instances of "objects.

If you can understand and use one simple use of "injection" here, you are going to make progress to make yourself ready to handle more complex .Net programming in the future.

Another way is to put a Public Property in the UserControl that can be set in code from the MainForm. In the UserControl something like :

private frmForm ParentForm;

public frmForm UCParentForm
{
    set { ParentForm = value; }
}

So then in your main form's code, perhaps in the Load event like this :

private void frmForm_Load(object sender, EventArgs e)
{
    TheUserControl.UCParentForm = this;
}

or when you need to, you set the UserControl's 'ParentForm property once. So you have eliminated using the method 'FindForm().

In this case, if you only want access to a specific control on the UserControl's Parent Form, like a TabControl, you might consider that you want to make the Property you set of type TabControl, rather than Form : the same coding technique shown above can be used in the UserControl :

private TabControl mainFormTabControl;

public TabControl MainFormTabControl
{
    set { mainFormTabControl = value; }
}

imho, it is when you are creating UserControls dynamically at run-time, using an overloaded constructor, as Wes suggests, is the best strategy. And using overloaded constructors has many, many others uses in .NET that you'll get into.

good luck !

BillW