Designer generated code problem with IContainer components

Locked Designer generated code problem with IContainer components

  • 2010年7月21日 8:11
     
      包含代码

    I'm having trouble with the code that is generated automatically by the WinForms designer, sometimes it generates code that uses the 'components' (IContainer) in the constructors of disposable components and other times it doesn't.

    Here is some code. First the code that is generated in a clean new project with just one component on the form:

      private void InitializeComponent()
      {
       this.components = new System.ComponentModel.Container();
       this.errorProvider1 = new System.Windows.Forms.ErrorProvider(this.components);
       ((System.ComponentModel.ISupportInitialize)(this.errorProvider1)).BeginInit();
       this.SuspendLayout();
       // 
       // errorProvider1
       // 
       this.errorProvider1.ContainerControl = this;
       // 
       // Form1
       // 
       this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
       this.ClientSize = new System.Drawing.Size(315, 262);
       this.Name = "Form1";
       this.Text = "Form1";
       this.DoubleClick += new System.EventHandler(this.Form1_DoubleClick);
       ((System.ComponentModel.ISupportInitialize)(this.errorProvider1)).EndInit();
       this.ResumeLayout(false);
      }
    
    Now the code that is generated when the designer does no recognise the 'components' variable:

      private void InitializeComponent()
      {
       this.errorProvider1 = new System.Windows.Forms.ErrorProvider();
       ((System.ComponentModel.ISupportInitialize)(this.errorProvider1)).BeginInit();
       this.SuspendLayout();
       // 
       // errorProvider1
       // 
       this.errorProvider1.ContainerControl = this;
       // 
       // Form1
       // 
       this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
       this.ClientSize = new System.Drawing.Size(321, 262);
       this.Name = "Form1";
       this.Text = "Form1";
       this.DoubleClick += new System.EventHandler(this.Form1_DoubleClick);
       ((System.ComponentModel.ISupportInitialize)(this.errorProvider1)).EndInit();
       this.ResumeLayout(false);
      }
    

    You can see that the errorProvider's constructor is not passed the 'components' variable, neither is 'components' initialized.

    Why might this be happening? Should I be woried about this?

    Thanks,

    Martin.


    Been programming since 1982, still going

全部回复

  • 2010年7月21日 13:08
    版主
     
     

    Not every control needs or requires access to the parent container (the components field).  In fact most controls don't.  Almost every control (at least the framework ones) exposes a ctor that accepts the container as a parameter.  This allows the control to have access to the container if it needs it.  If a control doesn't care about the container list then the designer won't bother generating the code to create one.  The general guideline is that control's don't care about the container (because it can access it via a separate property) but components do (because they don't have access to the parent).

    Now, the default generated code for a new form will contain the container declaration and initialization.  When you add a control to the form and the container is defined and it contains a ctor that accepts a container then the designer will hook everything up.  If the container field does not exist then things work a little differently.  Depending upon the control/component the designer might or might not generate the container field.  I'm not really sure what the designer uses to determine this need.  It might be hard-coded.  Nevertheless inserting a HelpProvider won't generate the container field but inserting an ErrorProvider will. 

    Ideally the container field should be defined so that components that need it can use it but most components won't fail if it doesn't exist.  The easiest way to generate it is to drop an ErrorProvider on the form and then delete the provider.

    Michael Taylor - 7/21/2010
    http://msmvps.com/blogs/p3net

  • 2010年7月22日 6:44
     
     

    Michael:

    Thanks for the detailed answer, much appreciated. This has given me a little more insight into how/why the 'components' container exists.

    What I can't figure out is why the designer is doing 2 different things with the same components. In the first code snippet, the components variable is used by the designer, but a little later is isn't . Why might this be? Is it possible it's a bug?

    Finally, is the second code snippet correct? Yes, it compiles and no warnings are given, but what are the side-effects of the ErrorProvider not being constructed with the components variable?

    Thanks for your insight, regards,

    Martin.


    Been programming since 1982, still going
  • 2010年7月22日 13:40
    版主
     
     已答复

    Either code will work. 

    Quick review of components vs controls.  When you're creating a new class that you want to use in a form you have to decide what base class it will have.  If the class will have a UI (ie. TextBox) then it should be a control.  If it doesn't need a UI then it can be a regular old class.  If the class extends existing controls (ie. ErrorProvider) then it generally implements IExtenderProvider.  This allows it to "attach" to other types.  The only problem with IEP is that, generally speaking, you want users to be able to drag and drop said class onto a form so they don't have to write the code by hand.  Enter Component

    Component (from which Control derives) handles the non-UI interaction with the parent container (if any).  All this class really does is provide some support to derived types for working with the parent container.  Components do not require that they be housed in a container.  For example it is pretty common to just create and use an instance of BWC directly in code rather than attaching it to a form.  Controls cannot do that.  Bringing it full circle, most (or perhaps all) IEPs derive from Component just so they can show up in the toolbox (which shows components+).  This is a convenience feature more than a requirement.  As a result most components will work without a parent container being passed.

    In fact the defacto implementation of a component that accepts a container is to simply add the component to the container's list.  It is the exact inverse of what you might expect (where the component uses the container).  Component has a seldom used property called Site of type ISite.  This can be set after the component is created.  The ISite interface allows access to the container associated with the site (the form).  For the few components that really, really need the parent container they can use the site to get it (if it was set).  I've only seen one or two components that ever really needed the parent container and this was strictly because they needed to find other components.

    Finishing up, is it a bug?  Not sure.  In general if you delete the component field declaration and creation then the designer should consistently recreate it for the same component (but might do different things for different components).  However the designer will probably not do anything if it already exists.  You'd have to identify the reproducible steps needed before we could determine if it is a bug or not.  I'd also be interested in knowing if it occurs in VS2010 as this is the only version that would be patched in the future.

    Michael Taylor - 7/22/2010
    http://msmvps.com/blogs/p3net

  • 2010年7月26日 6:19
    版主
     
     

    Hello

     

    I am writing to check the status of this thread in your side. If you have any further questions or concerns on this, please feel free to post back

     

    Best regards,

    Harry Zhu- MSFT

    MSDN Subscriber Support in Forum

    If you have any feedback of our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
  • 2010年10月31日 12:52
     
     

     

    I am resurrecting this thread because I have a similar problem. I am seriously considering going back to VS 2008 because of this problem, unless I see that Microsoft can track this bug down, because it's a serious show-stopper. If I just open a form, make a small change and save the form, and I can't rely on nothing getting f*ed up by the designer, then it's just not a usable product.

    Simply stated, here's the ways this bug manifests:
    1. Sometimes the designer will generate code that does not initialize "this.components", but it still passes it to the constructors of some of the components. Obviously this causes a "null value" exception.
    2. This will sound like #1 but it's different. Sometimes not only won't the designer initialize "this.components", but it will only initialize components that have a default constructor. Components that don't have a default constructor don't get initialized at all. Obviously this is a real problem also.
    While I was tracking this down and trying to fix it, I had two components that didn't have the default constructor (at least 2 that I noticed right away -- there might have been more that I didn't notice). I modified one of them to have a default constructor, and when I tried testing with it, the problem went away -- the designer started generating correct code that initializes "this.components", and passed it to the other component that didn't have a default constructor. It doesn't seem likely that my adding a default constructor to one component (but not the other) could have solved the problem, so it's probably just coincidence, but I wanted to be as thorough as possible in my reporting here.

    I can not reproduce this consistently, and I cannot supply a test case. It's proof for the Heisenberg uncertainty principle: The more I try to track it down, the more it goes away. But when will it come back is the real question.

    By the way, this was on a UserControl -- actually a Dev Express 2010.1 XtraUserControl, which serves as a base for other user controls.

    Environment:

    • Visual Studio 2010 Ultimate (from MSDN license)
    • Windows XP
    • Latest version of ReSharper
    • Dev Express 2010.1 (also have 7.2 and 6.2 installed, but this form used 2010.1)
    • T4 Editor from Tangible
    Dan Thomas
  • 2011年1月16日 21:10
     
     已答复
    If anyone is interested, I may have found a cause for the IContainer field being removed from code generation. I disabled the option Tools > Options > Windows Forms Designer > Optimized Code Generation. After I set this to false and restarted VS2010, when moving/adding/removing components to cause a Designer regeneration, the IContainer object appeared on all of the forms where it disappeared. Disclaimer: Your mileage may vary, this option isn't documented very well and does not describe what it exactly optimizes.
  • 2011年7月5日 14:38
     
      包含代码
    thx @jack.8514 seems to work. before 50% chance to have not the: this.components = new System.ComponentModel.Container(); and more funny is, that u have to use ISite in designtime, cause the Designer will never call the constructor with Icontainer args. He always seem to use the normal none args. So if you not check the *.Designer.cs everytime after changing something, you will know in runtime... I need the IContainer constructor for my components to find other same comps. And yes i know i can do this via reflextion, but i wont do :) the other hard way but without reflextion = singleton instance and register what you later want use again. But IContainer seems to be the best way for me.
  • 2011年12月9日 10:26
     
     
    Saved my day jack!  Good work!
  • 2012年2月22日 0:32
     
     

    This seems to have fixed my problem. Thanks!


    Steve