locked
Run code after inherited form has initialized all controls RRS feed

  • คำถาม

  • I have a base form class. I have some code I want to run almost immediately after the derived class has initialized all of its controls. If I use OnLoad it is too late.

    I have been getting around this by specifically calling the code in the derived class:

     public partial class BaseForm:System.Windows.Forms.Form
      {
        public BaseForm()
        {
          InitializeComponent();
    
          //can't call AfterInitialize() here as the derived form has not jet intialized its controls
        }
    
        protected void AfterInitialize()
        {
          //run some code here
        }
    
        protected override void OnLoad(System.EventArgs e)
        {
          base.OnLoad(e);
    
          //calls the code to late, not until a Form.Show method is used
        }
    
      }
    
      public partial class DerivedForm : BaseForm
      {
        public DerivedForm():base()
        {
          InitializeComponent();
    
          base.AfterInitialize(); //calling here, but must be manually implemented in all derived forms, not my ideal solution
        }
      }

    Any suggestions on the best way to go about this? Am I missing something obvious? I am using VS 2008 .Net 3.5, Thanks

    2 มิถุนายน 2553 16:24

คำตอบ

  • Hello, 

    first i have to agree with Rudy that if the Load event is already too late then it can't be done using inheritance. Factory pattern should allow you to instantiate the form and always call the AfterInitialize() method, and as Ahmed said so would introducing a controller. Then again, IMO, if there is a limited number of developers working on that project, your original solution, with proper documentation would look good enough for me. Maybe checking in the base form onload if the AfterInitialize() has actually been called and throwing exception if not, would decrease the risk of someone forgetting to call it. Again, depends on the scale of your project.

     

    Only other idea that comes to mind, which might facilitate the process of deriving from the base form is to change the way your form serializes to code and generate call to AfterInitialize() at the end of the InitializeComponent() method. This way the developer would not have to do it himself, it would be automated. That approach would also have its drawbacks, such as InitializeComponent can be manually edited, or someone can inherit the base form manually and never open it in designer, thus not even creating the designer file.

     

    Best regards,
    Vladimir

    • ทำเครื่องหมายเป็นคำตอบโดย Helen Zhou 9 มิถุนายน 2553 9:10
    5 มิถุนายน 2553 13:29
  • Bad Design.  You are designing towards inheritance, when you should be favoring composition.


        class Program
        {
            static void Main(string[] args)
            {
                SomeDerived sd = new SomeDerived(); // breakpoint here
            }
        }

        class SomeBase
        {
            protected int num1;
            public SomeBase()
            {
                this.InitializeComponent();
            }

            private void InitializeComponent()
            {
                num1 = 10;
            }
        }

        class SomeDerived : SomeBase
        {
            protected int num2;
            public SomeDerived()
            {
                this.InitializeComponent();
                // you want to run code from SomeBase here, right?
                return;
            }

            private void InitializeComponent()
            {
                num2 = 10;
            }
        }

     

    Set a breakpoint where I indicated and step through the code until the SomeDerived instance is fully constructed.   See how a SomeBase object is constructed first.  Any and all code that runs SomeBase is run prior to any code being executed in SomeDerived. 

    There is no way to add code to the base class that will automatically when you wish it.  It might be possible with a Factory method, though.  But, I think the basic design is flawed and bound to cause headaches.

     


    Mark the best replies as answers. "Fooling computers since 1971."
    • ทำเครื่องหมายเป็นคำตอบโดย Helen Zhou 9 มิถุนายน 2553 9:12
    3 มิถุนายน 2553 15:10
  • Let the base class subscribe to the Form.Shown event.  There is no guarantee that all controls are initialized in Form.Load.  The derived class could be doing its' own stuff to initialize controls.  Actually, the same holds true for Shown.

    Mark the best replies as answers. "Fooling computers since 1971."
    • ทำเครื่องหมายเป็นคำตอบโดย Helen Zhou 9 มิถุนายน 2553 9:10
    4 มิถุนายน 2553 20:24

ตอบทั้งหมด

  • There is no good way to force a derived class into calling a specific method in a derived class method. 

    The derived class must add the method call to itself, which you have shown.

    "Favor composition over inheritance."


    Mark the best replies as answers. "Fooling computers since 1971."
    2 มิถุนายน 2553 17:25
  • I was not so interested in forcing the derived class to call the method, as having the method called by the base class at the correct time. If there was an InitiliazationComplete event or overridable OnInitialization method I would use that. In the end "you just can't do that" might be the answer I have to accept, but I want to make sure. I am also open to suggestions on other ways of accomplishing my task. The code needs to iterate some of the controls in the derived form after they have been initialized but before their properties might be changed by other code.

    3 มิถุนายน 2553 14:45
  • Bad Design.  You are designing towards inheritance, when you should be favoring composition.


        class Program
        {
            static void Main(string[] args)
            {
                SomeDerived sd = new SomeDerived(); // breakpoint here
            }
        }

        class SomeBase
        {
            protected int num1;
            public SomeBase()
            {
                this.InitializeComponent();
            }

            private void InitializeComponent()
            {
                num1 = 10;
            }
        }

        class SomeDerived : SomeBase
        {
            protected int num2;
            public SomeDerived()
            {
                this.InitializeComponent();
                // you want to run code from SomeBase here, right?
                return;
            }

            private void InitializeComponent()
            {
                num2 = 10;
            }
        }

     

    Set a breakpoint where I indicated and step through the code until the SomeDerived instance is fully constructed.   See how a SomeBase object is constructed first.  Any and all code that runs SomeBase is run prior to any code being executed in SomeDerived. 

    There is no way to add code to the base class that will automatically when you wish it.  It might be possible with a Factory method, though.  But, I think the basic design is flawed and bound to cause headaches.

     


    Mark the best replies as answers. "Fooling computers since 1971."
    • ทำเครื่องหมายเป็นคำตอบโดย Helen Zhou 9 มิถุนายน 2553 9:12
    3 มิถุนายน 2553 15:10
  • I fully accept that my design might be bad, but:

    Why is it bad?

    What are my alternatives? How should I change my design?

    I were to override OnLoad() and put my code in there, would that be bad design?

    ASP.Net has an OnInit() is using that bad?

    4 มิถุนายน 2553 20:21
  • Let the base class subscribe to the Form.Shown event.  There is no guarantee that all controls are initialized in Form.Load.  The derived class could be doing its' own stuff to initialize controls.  Actually, the same holds true for Shown.

    Mark the best replies as answers. "Fooling computers since 1971."
    • ทำเครื่องหมายเป็นคำตอบโดย Helen Zhou 9 มิถุนายน 2553 9:10
    4 มิถุนายน 2553 20:24
  • By initialized I mean initializecomponent() has been run. Since this is run in the constructor it is complete before Load or Show is called.

    4 มิถุนายน 2553 21:36
  • Hi Matt,

    I remember I needed to do something similar to what you need to do. But in our case we had a controller (a class) that creates the Form, Call "AfterInitialize", then show the form.

    Cheers

    5 มิถุนายน 2553 4:22
  • Hello, 

    first i have to agree with Rudy that if the Load event is already too late then it can't be done using inheritance. Factory pattern should allow you to instantiate the form and always call the AfterInitialize() method, and as Ahmed said so would introducing a controller. Then again, IMO, if there is a limited number of developers working on that project, your original solution, with proper documentation would look good enough for me. Maybe checking in the base form onload if the AfterInitialize() has actually been called and throwing exception if not, would decrease the risk of someone forgetting to call it. Again, depends on the scale of your project.

     

    Only other idea that comes to mind, which might facilitate the process of deriving from the base form is to change the way your form serializes to code and generate call to AfterInitialize() at the end of the InitializeComponent() method. This way the developer would not have to do it himself, it would be automated. That approach would also have its drawbacks, such as InitializeComponent can be manually edited, or someone can inherit the base form manually and never open it in designer, thus not even creating the designer file.

     

    Best regards,
    Vladimir

    • ทำเครื่องหมายเป็นคำตอบโดย Helen Zhou 9 มิถุนายน 2553 9:10
    5 มิถุนายน 2553 13:29
  • Without a better example of what you are trying to do, we can't really suggest an alternative.

    Without knowing what it is that you are trying to do I can only offer the following suggestion (which may also be no good).

      public BaseForm()
      {
       InitializeComponent();
    
       this.CreateControl();
    
       AfterInitialize();
      }
    

    If  this doesn't help, then providing a simple example showing the failure may help to provide a solution.


    Mick Doherty
    http://dotnetrix.co.uk
    http://glassui.codeplex.com
    6 มิถุนายน 2553 10:53
  • There is no way to force a developer to add a certain line of code into a derived class constructor.

    Mark the best replies as answers. "Fooling computers since 1971."
    7 มิถุนายน 2553 13:07
  • Thank you Vladimir, this is what I am going to do. I also think Ahmed's answer is helpfull. I would have marked Vladimir's as the answer but the moderator got to it first.  It also worth noting that I was trying to treat InitializeComponent() as if it were part of the control or form class when it is really just a function of the designer. Which explains why I will not find the events I am looking for and it is not part of the control lifecycle.

    10 มิถุนายน 2553 16:19
  • Hi, I know is late, but maybe is useful for someone...

    You can override ResumeLayout(), it is called inside InitializeComponent() after all the controls is added to the form and the form have all values from Designer assigned. And as ResumeLayout is called inside InitializeComponent, The designer will show also controls you add to form inside of the new ResumeLayout.

    Example:

            bool initEnd = false;
           public new void ResumeLayout()
            {
                if (!initEnd)
                {
                    initEnd = true;
                    AfterInitialize();
                }
                base.ResumeLayout();
            }
            public new void ResumeLayout(bool performLayout)
            {
                if (!initEnd)
                {
                    initEnd = true;
                    AfterInitialize();
                }
                base.ResumeLayout(performLayout);
            }

    11 ธันวาคม 2562 11:21