views:

1152

answers:

4

I created a class derived from System.Windows.Forms.ContextMenuStrip class, not as a user control, just a plain .cs class with a constructor and one event handler.

When I drag this class from the Toolbox onto the designer, it creates a private member for it and a couple of properties, but does not instantiate an object.

Thus, at runtime I get "Object reference not set to an instance of an object.", because the designer never generates the line:

this.searchGridContextMenu1 = new SearchGridContextMenu();

inside InitializeComponent.

It used to generate this line, as a matter of fact, I keep putting it back in from my Vault repository, but the designer just "eats it" again.

Update: I now tried creating a User Control using the same class and it has the same problem just doing that.

+1  A: 

Inheriting from the control isn't enough. This is the bare minimum that I had to implement to make it work:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WinForms
{
    class Foo : System.Windows.Forms.ContextMenuStrip
    {
        public Foo()
        {
            InitializeComponent();
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
        }

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }

        #endregion
    }
}
OJ
That doesn't seem to make any difference in the behavior.
Dennis
A: 

Are you using the default constructor to instantiate your derived class? I was having the same problem recently with a CustomControl I had designed. I needed the control to subscribe to an event published by another CustomControl, but only if it was on the app's main form and not if it was on a different form. So I added a constructor that accepted a bool to allow it to subscribe when it should and not when it shouldn't. But after I did this, every time I switched to the designer, Visual Studio complained that the control didn't exist and I would discover that it had removed the line in the .Designer.cs file that instantiated it. As soon as I refactored the control to achieve the same logic using the default constructor and a public method for subscribing, I stopped having the issue.

Bryan
I'm just using the default constructor. The control is really basic at this point.
Dennis
+3  A: 

I cross-posted this comment on another question but since this is related here it is again.

When a User Control won't load into the Visual Studio designer here is what you need to do. These instruction are for vb.net project but c# should be similar. Also, before doing this close all open windows (or at least the source and designer files of the control you are working on.)

One last thing. The FIRST thing you should do is ensure that restarting visual studio doesn't fix the problem. If not you can try the steps that follow. These instructions assume that the errant user controls are in control library project in visual studio. If not you should be able to adjust the directions a bit to get it to work but it is much easier when the control is in its own project.

Do the following:

  1. Make the control library your startup project.
  2. Open the properties for the control library project and click on the debug tab.
  3. Under Start Action click the Start external program option and browse to the Visual Studio executable.

NOTE: what this means is that when you run your solution it will fire up another instance of Visual Studio instead of actually running your solution. The First Instance of Visual Studion (INSTANCE_1) will "host" a second instance of visual studio (INSTANCE_2) when you run it.

  1. Run your solution. INSTANCE_2 will load.
  2. Switch back to INSTANCE_1.
  3. In INSTANCE_1 hit CTRL-ALT-E. This will open up the exceptions dialog box. Check On the THROWN column checkbox next to Common Language Runtime Exceptions.

NOTE: This will ensure that INSTANCE_1 will BREAK at ANY runtime error even if it is hit in a try block.

  1. Switch to INSTANCE_2. In Solution Explorer double-click to open the errant user control.

You should find that INSTANCE_1 OF Visual Studio should have stopped at the line of code that caused the designer to not load the control. Fix the code (which usually means testing for IsNot Nothing before references an object properties...but could mean other things.)

Also, sometimes I find that the control WILL load in INSTANCE_2 instead of breaking on an error in INSTANCE_1. In that case just stop debugging...close INSTANCE_2. Save/Restart INSTANCE_1 and your problem will often have gone away.

The lesson is this. User Control MUST be able to load/reference all objects and their members in order to load it into the designer. So for User Controls that will be placed onto other containers I will usually design events to notify the parent rather than trying to push objects into the child control.

Hope this helps for future reference on this old question.

Seth

Seth Spearman
+1 for restarting Visual Studio. The WinForms designer seems to become confused occasionally
Pedro
A: 

This problem occurs when InitializeComponent () crosses with base and inherited components. Solution is call the component's designer's method. Otherwise the base method will be called.

public Form()
{
    this.InitializeComponent(); 
    // base.InitializeComponent <-- default one is thisone
}
jack london