Invalidate() causes NULL POINTER exception on programmatically addedd control (c#) - c#

When I add a PictureBox to my form like this:
public partial class frmMain : Form
{
PictureBox _pb;
public formMain(){
_pb = new PictureBox();
formMain.Controls.Add(_pb);
}
//SOME METHOD
private void SomeMethod(){
_pb.Invalidate(); //NULL POINTER EXCEPTION
}
}
What's going on here? Is more needed to add a control to a form?
More info:
If I drag a picturebox to the "form designer" in visual studio C#, and name it _pb.
The above works. Are there additional steps to adding a control programmatically? More than just calling Form.Controls.Add(/some control/) ???

Really just a hunch, would need to see the full class and not just pieces of it. But one curiosity is that you have:
formMain.Controls.Add
in a constructor for formMain, where is the variable formMain defined (assuming this compiles). Don't you mean:
this.Controls.Add(_pb)
Not sure if this the problem, I suspect that the problem could also be that _pb is not defined by the time that it invalidate is called. Do you have any other constructors that are used that don't initialized _pb?

Related

How can I add a UserControl to a Panel on the main form?

I am working on project winForm application. Am trying to add a UserControl to a Panel on the main Form.
Here is what I did, I created a public static method on the Main Form so I can call it from anywhere without instantiating the main form.
public static void showUC(UserControl uc)
{
pnlContainer.Controls.Clear();
GC.Collect();
uc.Dock = DockStyle.Fill;
pnlContainer.Controls.Add(uc);
}
I am getting a compilation error "An object reference is required for a non-static field, method or property".
If I create the method as non-static the compilation error will be gone but I won't be able to call it without creating object of the main form.
What I want to achieve is to be able to open any UserControl inside the Panel 'pnlContainer' on my Main Form 'frmMain'.
When the main form loads I want to calling the method without instantiating the main form again. For example
public static void showUC(UserControl uc)
{
pnlContainer.Controls.Clear();
GC.Collect();
uc.Dock = DockStyle.Fill;
pnlContainer.Controls.Add(uc);
}
I want to be calling this method like this
frmMain.showUC(new ucClientList);
From another class.
Let me explain exactly what I want to achieve.
I have a Form Control named 'frmMain' which contain a panel control named 'pnlContainer' and I want to be loading UserControl into it, I have UserControl1 and UserControl2.
Now I run the program and the frmMain loads, I wants to add UserControl1 to the panel pnlContainer and I have a button inside the UserControl1 which is supposed to load UserControl2 into the same panel on the Main Form when I click on it. How do I implement this?
If you really want to do so you can write your code like this:
public static void showUC(Control uc, Control pnlContainer)
{
pnlContainer.Controls.Clear();
uc.Dock = DockStyle.Fill;
pnlContainer.Controls.Add(uc);
}
and call it like this:
showUC(new someControlType(), someControlContainer);
No need to call the GC; do leave it to the system to clean up matters!
And a static function needs to know all objects it is supposed to work with, so you need to pass in the container as well.
Finally new needs paranthesis with (optionally) parameters..
I made the signature of the new control Control so you can pass in any control; and for even more flexibility the container is also Control. Note that this still allows you to pass in the Form! Of course you may want to go back to more restricted types..
Update
If you want to switch between two UserControls that both sit in the same container you can use this function:
public static void showUC(Control uc)
{
foreach (Control ctl in uc.Parent.Controls) ctl.Visible = ctl != uc;
}
It can access the other one via the Parent of the calling one. In the UC write:
private void button1_Click(object sender, EventArgs e)
{
Form1.showUC(this);
}
You could modify to actually create the UserControls instead of Hiding/Showing them; but you would lose their state each time..
If that is what you want, you may consider changing the name to something like switchUcFrom or showOtherUC; after all it doesn't 'show this' but rather 'hide this and show the other UC'

Set form as Parent throw exception "Top-level control cannot be added to a control"

I want to access variables of a form from another form. On clicking a button inside my Main form, I want to set my Main form as Parent, then bring up another form (child form) wherein I will access variables of the Main form. My click handler is as follow:
private void btnSystem_Click(object sender, EventArgs e)
{
Form_EnterPassword EP = new Form_EnterPassword();
EP.Parent = this; //error: Top-level control cannot be added to a control
EP.ShowDialog();
}
It compiles fine without any error. However, when I run the Main form and click on the System button, it throws me an exception. I do something similar in another code (not mine) with the same button click, and encounter no error (just with setting Main form as Parent).
What am I doing wrong? Is there something in my Main code that cause this?
Best way would be to use EP.ShowDialog(this) and later use Owner property.
You need the EP.TopLevel property to be set to false. It will let you to set a parent to it.
Further reading.
In case you only want to access variables and controls of another form, then maybe you can reach it in other ways, not trough a Parent relationship.
OK,
apparently the way to do it is to call
Form_Child.ShowDialog(this)
and then I can call
FromParent_aVariable = ((Form_Parent)this.Owner).aVariable;
or if I define aVariable in the namespace Properties then
FromParent_aVariable = NameSpace.Properties.Settings.Default.aVariable;
there are two ways.
Form_EnterPassword EP = new Form_EnterPassword();
EP.MdiParent = this;
EP.Show();
try this way, it helps for me. you need to set principalform as isMdicontainer = true at the form properties
I had a similar situation recently.
I was attempting something similar but by controlling the Child Forms from a different class.
Note(s):
You're trying to set the Child Form(s) "TopMost" to something that does not allow it.
In this case the "MdiContainer".
To accomplish this:
• Disable MainForm "isMdiContainer" property (its use is kind of obsolete anyway).
• Set the Form(s) TopMost properties to true.
• You should now be able to accomplish your feature.
**Code Example:**
/* On your Main Form Class */
private void btnSystem_Click(object sender, EventArgs e)
{
// Instantiate the Form_EnterPassword by passing the MainForm
Form_EnterPassword EP = new Form_EnterPassword(this);
EP.Show(); // No longer as modal Form to display in front.
}
/* Under your EnterPassword Form Class */
// Do not create a new Instance of MyMainForm.
// You want to use the same thread as your MainForm
private MyMainForm mainForm;
/* Constructor */
public Form_EnterPassword(MyMainForm form)
{
mainForm = form;
this.Owner = mainForm; // "this" refers to the: EnterPassword Form.
}
Remarks:
The only additional thing that you (may) have to do, (to achieve perfection) is to check the MainForm > WindowState; and create a code block to minimize or bring the Forms to their specific state.
i.e:
if (WindowState == FormWindowState.Minimized)
{ /* Code to Minimize all the Child Forms. */ }
else { /* Code to bring all Forms to their "Normal" State */ }
Writing this way, made the dialog display on the center of the parent form.
Form_Child.StartPosition = FormStartPosition.CenterParent;
Form_Child.ShowDialog(this);

How can I access the MainForm from within a class in a separate project?

I have two projects in this solution: ProjectA and ProjectB. ProjectA is the main start-up project, and has a reference to ProjectB.
ProjectA has a file called MainForm.cs, which contains a textbox and the main UI.
ProjectB has a class inside Shapes.cs, containing a particular structure we're using. Shapes.cs contains an event that is fired when the user changes some text for that object.
What I need to do is catch that text and set a textbox in MainForm.cs to that text. Is there a way we can do that? Basically I don't see any reference to the main form inside Shapes.cs. I would like to do something like this:
( Shape1.Parent as MainForm ).TextBox1.Text = Shape1.Name;
, assuming the user types a string that gets stored in Shape1.Name. I need to escalate it to the main form.
I have searched around for other questions, and the closest lead I found was Matt Hamsmith's answer on this question. But if it is a good approach I should follow, I do not know how to assign an event handler in the main form to an event in the separate class. I would appreciate any help.
Thanks.
If the form is made up child controls, it should be listening to events on those controls, rather than the controls trying to cast their parent as a particular type. Doing that means your control will only ever work on that Form. It breaks encapsulation.
Listen to an event like this:
public class MainForm : Form
{
Shape _shape1 = new Shape();
public MainForm()
{
InitializeComponent();
_shape.ShapeNameChanged += HandleShapeNameChanged;
}
public void HandleShapeNameChanged(object sender, ShapeChangeEventArgs e)
{
textBox1.Text = e.NewName;
}
}
public class Shape
{
public event EventHandler<ShapNameChangedEventArgs> ShapeNameChanged;
}
I've left it for you to:
Define the ShapeNameChangedEventArgs object to contain whatever state you want it to.
Invoke the event when something on your control changes.
Good luck!

Winform inheritance and default form size

An application I'm working will have a number of forms with a lot of shared functionality. For instance, each form will have a DataGridView, many of the same buttons, much of the same UI code and so on.
I'd like to implement this by creating a base version of this common form, subclass it for all these very-similar-but-not-quite-the-same child forms, and tack on whatever additional controls and features I need for each of them.
I've already figured out that it helps to make the base form's controls protected because this allows things like anchoring to work propertly. However, I have yet to find a way to automatically make the derived forms the same size as the base form.
Experience tells me there should be a simple way to do this. While it's not much of a problem to just type in the required size by hand for every derived form right after creating it, I'd prefer to make everything as clean, simple, and automatic as possible.
I find it interesting that your derived forms do not automatically inherit the size from their base form, because this should work without you having to do anything about it.
Suspected cause of your problem:
I suspect your problem results from the fact that you're using Visual Studio's Forms Designer to edit the forms. Whenever you've edited a form, Windows Forms Designer generates the required code in the InitializeComponent method of your forms. Among all the generated code are assignments that set a form's size, even if it is identical to the base form's size. Therefore you might have to manually comment out those assignments if you want your derived form to have the same size as the base form, even when you change the base form's size after creating the derived forms. (However, I don't know if that might lead to further problems with the controls' positioning & layouting.)
// Code to be commented out in your derived form's InitializeComponent method:
this.AutoScaleDimensions = new System.Drawing.SizeF(...);
this.ClientSize = new System.Drawing.Size(...);
Once these lines are commented out, the size as set in your base form's InitializeComponent will be used for the derived form.
A workaround solution:
You can do the following so that you don't have to manually comment-out designer-generated code every time you've edited a form:
Create an form derived from your base form; let's call it FrozenBaseForm. You will derive all other forms from this class instead of directly from the base form. Now, in this "intermediate" class, you define a new property ClientSize:
public class FrozenBaseForm : BaseForm
{
new public SizeF ClientSize
{
get { return base.ClientSize; }
set { }
}
}
This will cause all assignments to ClientSize to have no effect at all and therefore preserve the size from the base form. This feels like a hack to tell the truth, but it seems to work. You might have to hide the Size property in the same way btw.
As said, derive your forms from FrozenBaseForm instead of from BaseForm directly:
public class DerivedForm1 : FrozenBaseForm { ... }
public class DerivedForm2 : FrozenBaseForm { ... }
...
Another option (last resort if all else fails):
As a last resort, you could simply forget about the Forms Designer and just define the derived forms manually in the code editor (though I personally would not want to do this):
public class DerivedForm : BaseForm
{
public DerivedForm()
{
// make all necessary changes to the base form:
...
}
}
public partial class derivedForm : baseForm
{
public derivedForm()
{
InitializeComponent();
this.Width = base.Width;
this.Height = base.Height;
}
}
Why not make the BaseForm set the size of itself?
public partial class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
// you could hardcode these or retrieve these values from a
// config file or something
this.Width = 640;
this.Height = 468;
}
}
Wouldn't this do what you want?

Winform & user control - C#

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
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 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 !
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?

Categories

Resources