Late binding to an event - please provide source code solution, no links.

Tom Knowlton
Tom Knowlton used Ask the Experts™
on
I have some dynamic controls that I am loading in at runtime.  These controls have events they can raise, which I will NOT know about ahead of time, but I will have methods with matching signatures.  What I want to accomplish is to tie those events to the handlers.

Look for my comments in the code below to see what method I want to attach to.  I don't want to FIRE the event...I just want to become a listener.


When you reply...please provide working C# source code ONLY!!   Please....NO LINKS TO ARTICLES OR WEBSITES!


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Reflection;

namespace Campus_Webstore
{
    public partial class SiteManager : System.Web.UI.Page
    {
        protected void Page_PreInit(object sender, System.EventArgs e)
        {
            this.MasterPageFile = "~/contentfiles/" + Page.StyleSheetTheme + "/TCSSite.Master";
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                GlobalMethods.InitControlList();


                string layout = "";

                if (Request.QueryString["layout"] != null)
                {
                    layout = Request.QueryString["layout"] as string;
                }
                else
                {
                    layout = "default";
                }

                DisplayLayout(layout);

            }
           
        }

        private void DisplayLayout(string layout)
        {
            GlobalMethods.divlayoutgencontrols.Clear();

            StreamReader sr = new StreamReader(Server.MapPath("~/CustomLayouts/") + Page.StyleSheetTheme + "/" + layout + ".xml");
            string filecontents = sr.ReadToEnd();
            string countstr = GlobalMethods.ExtractValueFromXML(filecontents, "<count>");
            int count = Convert.ToInt32(countstr);
            string tempGUID = "";
            string settings = "";
            string[] settingsarray = new string[count];

            for (int i = 1; i <= count; i++)
            {
                settings = GlobalMethods.ExtractValueFromXML(filecontents, "<" + i.ToString() + ">");
                settingsarray = settings.Split(',');

                Control c = LoadControl("~/UserControls/" + settingsarray[0] + ".ascx");

                Type controlType = c.GetType();
                BindingFlags myBindingFlags = BindingFlags.Instance | BindingFlags.Public;
                EventInfo[] myEvents = controlType.GetEvents(myBindingFlags);

                for(int j = 0; j < myEvents.Count(); j++)
                {
                    string eventName = myEvents.ElementAt(j).Name;
                    if (eventName == "NameAvailabilityEvent")
                    {
                        //Delegate d = Delegate.CreateDelegate(myEvents.ElementAt(j).EventHandlerType, this,myEvents.ElementAt(j).Name);
                        //c.GetType().GetEvent(eventName).AddEventHandler(c, d);






                        //btn is "c"

                        EventInfo evt = c.GetType().GetEvent(myEvents.ElementAt(j).Name);
                        MethodInfo handler = GetType().GetMethod("SomeHandler");




//////////////////////////////   HERE IS WHERE THE BINDING MIGHT TAKE PLACE  /////////////////////////////////





                        evt.AddEventHandler(c, Delegate.CreateDelegate(
                                evt.EventHandlerType, this, handler));

                    }
                }              

                c.Visible = true;
                tempGUID = Guid.NewGuid().ToString();
                c.ID = settingsarray[0] + tempGUID + i.ToString();

                GlobalMethods.divlayoutgencontrols.Add(c);
            }

            foreach (Control c in GlobalMethods.divlayoutgencontrols)
            {
                sitemanagercontrolsdiv.Controls.Add(c);
            }


            Master.CalcCartTotal();
            Master.ShowCat();
            Master.DisplayUser();

        }



////////////////////   I WANT TO BIND THE EVENT TO THIS METHOD  /////////////////////////////////////////

        public void SomeHandler(bool wasavailable)
        {
       //     create_new_user.ShowRotatingArrow(true);

            if (wasavailable)
            {
                //LabelResultMessage.Text = "That username is available.";
                //ButtonClosePanelMessage.Visible = true;
                //PanelMessage.Visible = true;
            }
            else
            {
                //LabelResultMessage.Text = "That username is already taken.";
                //ButtonClosePanelMessage.Visible = true;
                //PanelMessage.Visible = true;

            }

          //  create_new_user.ShowRotatingArrow(false);
        }
    }
}

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2012
Top Expert 2008

Commented:
Tom,

One possibility, off the top of my head, would be to use reflection, as shown in this MSDN article:

How to: Hook Up a Delegate Using Reflection
http://msdn.microsoft.com/en-us/library/ms228976.aspx

Assembly assem = Assembly.GetExecutingAssembly();

Type tExForm = assem.GetType("ExampleForm");
Object exFormAsObj = Activator.CreateInstance(tExForm);

EventInfo evClick = tExForm.GetEvent("Click");
Type tDelegate = evClick.EventHandlerType;

MethodInfo miHandler = 
    typeof(Example).GetMethod("LuckyHandler", 
        BindingFlags.NonPublic | BindingFlags.Instance);

Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);

MethodInfo addHandler = evClick.GetAddMethod();
Object[] addHandlerArgs = { d };
addHandler.Invoke(exFormAsObj, addHandlerArgs);

Open in new window


Tom KnowltonWeb developer

Author

Commented:
Hi TLO,

For some reason the event does not fire.

Can you take a look at my current code?

You should be able to create a small project using the information I give below that will duplicate this problem.  I've been staring at this for so long I don't think I can be very useful, meaning, I have tried and failed at getting this to work over a period of days, trying every bit of advice and permuation I can glean from websites.

What is the difference between what the MSDN article is doing and what I'm doing?

Thanks from a very frustrated programmer.  :)


MARKUP FOR THE USER CONTROL (.ascx):

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TestRaiseEvent.ascx.cs" Inherits="Campus_Webstore.TestRaiseEvent" %>
<asp:Button ID="Button1" runat="server" Text="Raise an Event now" onclick="Button1_Click" />

CODE-BEHIND FOR THE USER CONTROL (.ascx):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Campus_Webstore
{   
    public delegate void ButtonWasClicked (string message);
   
    public partial class TestRaiseEvent : System.Web.UI.UserControl
    {
        public event ButtonWasClicked ButtonClickedEvent;
              
        protected void Page_Load(object sender, EventArgs e)
        {
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            if (ButtonClickedEvent != null)
            {
                ButtonClickedEvent("some message");
            }         
        }
    }
}






Then, here is the main webform that will attempt to load the control above programmatically and discover and subscribe to the event that is raises:



MARKUP FOR THE WEB FORM  (.aspx)  [uses a master page]:

<%@ Page Title="" Language="C#" AutoEventWireup="true" CodeBehind="SiteManager.aspx.cs" Inherits="Campus_Webstore.SiteManager" %>


<%@ MasterType TypeName="Campus_Webstore.SiteWideMaster.TCSMasterBase" %>


<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<div id="sitemanagercontrolsdiv" runat="server">  
</div>
</asp:Content>






CODE-BEHIND FOR THE WEB FORM (.aspx)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Reflection;



//The control collection cannot be modified during
//DataBind,
//Init,
//Load,
//PreRender or
//Unload phases.

namespace Campus_Webstore
{
    public partial class SiteManager : System.Web.UI.Page
    {
        protected void Page_PreInit(object sender, System.EventArgs e)
        {
            this.MasterPageFile = "~/contentfiles/" + Page.StyleSheetTheme + "/TCSSite.Master";

            GlobalMethods.InitControlList();


            string layout = "";

            if (Request.QueryString["layout"] != null)
            {
                layout = Request.QueryString["layout"] as string;
            }
            else
            {
                layout = "default";
            }

            LoadControls(layout);

        }
             
        protected void Page_Load(object sender, EventArgs e)
        {
            //if (!Page.IsPostBack)
            //{
            //    GlobalMethods.InitControlList();


            //    string layout = "";

            //    if (Request.QueryString["layout"] != null)
            //    {
            //        layout = Request.QueryString["layout"] as string;
            //    }
            //    else
            //    {
            //        layout = "default";
            //    }

            //    LoadControls(layout);
            //}       

            Master.CalcCartTotal();
            Master.ShowCat();
            Master.DisplayUser();

        }


        //The control collection cannot be modified during
        //DataBind,
        //Init,
        //Load,
        //PreRender or
        //Unload phases.

        protected void Page_LoadComplete(object sender, EventArgs e)
        {         
                sitemanagercontrolsdiv.Controls.Clear();

                try
                {
                    if (GlobalMethods.divlayoutgencontrols != null)
                    {
                        foreach (Control c in GlobalMethods.divlayoutgencontrols)
                        {
                            sitemanagercontrolsdiv.Controls.Add(c);
                        }
                    }
                }
                catch (Exception eee)
                {
                    string a = eee.Message;
                }
           
        }


        private void LoadControls(string layout)
        {
           
            StreamReader sr = new StreamReader(Server.MapPath("~/CustomLayouts/") + Page.StyleSheetTheme + "/" + layout + ".xml");
            string filecontents = sr.ReadToEnd();
            string countstr = GlobalMethods.ExtractValueFromXML(filecontents, "<count>");
            int count = Convert.ToInt32(countstr);
            string tempGUID = "";
            string settings = "";
            string[] settingsarray = new string[count];

            for (int i = 1; i <= count; i++)
            {
                settings = GlobalMethods.ExtractValueFromXML(filecontents, "<" + i.ToString() + ">");
                settingsarray = settings.Split(',');

                Control c = LoadControl("~/UserControls/" + settingsarray[0] + ".ascx");
                                           
                Type controlType = c.GetType();
               
                BindingFlags myBindingFlags = BindingFlags.Instance | BindingFlags.Public;
                EventInfo[] myEvents = controlType.GetEvents(myBindingFlags);

                for (int j = 0; j < myEvents.Count(); j++)
                {
                    string eventName = myEvents.ElementAt(j).Name;
                    if (eventName == "ButtonClickedEvent")
                    {
                        EventInfo evt = c.GetType().GetEvent(myEvents.ElementAt(j).Name);
                        MethodInfo handler = typeof(SiteManager).GetMethod("SomeHandler");
                        Delegate del = Delegate.CreateDelegate(evt.EventHandlerType, this, handler);
                        evt.AddEventHandler(c, del);                       
                    }
                }

                c.Visible = true;
                tempGUID = Guid.NewGuid().ToString();
                c.ID = settingsarray[0] + tempGUID + i.ToString();

                GlobalMethods.divlayoutgencontrols.Add(c);
            }         
        }

        public void SomeHandler(string message)
        {
            // do something
        }
    }
}



HERE ARE THE CONTENTS OF default.xml, a file which is read-in at runtime:

<count>1</count>
<1>TestRaiseEvent</1>

Open in new window

Most Valuable Expert 2012
Top Expert 2008

Commented:
We are interested in the below section--what does tracing tell you?  Are you getting events from myEvents?  Are you getting a reference to the "SomeHandler" method?

              BindingFlags myBindingFlags = BindingFlags.Instance | BindingFlags.Public;
                EventInfo[] myEvents = controlType.GetEvents(myBindingFlags);

                for (int j = 0; j < myEvents.Count(); j++)
                {
                    string eventName = myEvents.ElementAt(j).Name;
                    if (eventName == "ButtonClickedEvent")
                    {
                        EventInfo evt = c.GetType().GetEvent(myEvents.ElementAt(j).Name);
                        MethodInfo handler = typeof(SiteManager).GetMethod("SomeHandler");
                        Delegate del = Delegate.CreateDelegate(evt.EventHandlerType, this, handler);
                        evt.AddEventHandler(c, del);                       
                    }
                }

Open in new window

Starting with Angular 5

Learn the essential features and functions of the popular JavaScript framework for building mobile, desktop and web applications.

Most Valuable Expert 2012
Top Expert 2008

Commented:
What framework version are you using with the web site?
Tom KnowltonWeb developer

Author

Commented:
Bob,

I think it is 4.0XXXX

 about vs
Tom KnowltonWeb developer

Author

Commented:
We are interested in the below section--what does tracing tell you?  Are you getting events from myEvents?  Are you getting a reference to the "SomeHandler" method?

=================================

How do I find this out?

Trace dumps a bunch of stuff at the bottom of the screen, right?
Tom KnowltonWeb developer

Author

Commented:
As far as getting Events from myEvents..........the answer is definitely YES:


 my Events collection
Tom KnowltonWeb developer

Author

Commented:
It is this event I want to handle from that list:

{Campus_Webstore.ButtonWasClicked ButtonClickedEvent}
Most Valuable Expert 2012
Top Expert 2008

Commented:
1) You don't have the Ultimate edition, so Intellitrace is out

2) Trace does dump a lot of information, and is not useful

3) The words I should have used are "step debugging"--or stepping through the code, and inspecting important values.

4) If your web site is configured for 4.0, then you might be able to use the "dynamic" keyword when referencing the control.

5) I have not tried "dynamic" in an ASP.NET 4 web site, but theoretically it might work.

C# 4.0 dynamic Keyword
http://spradip.wordpress.com/2011/02/07/c-4-0-dynamic-keyword/
Tom KnowltonWeb developer

Author

Commented:
As far as a reference to the "SomeHandler" method....does this help?



 handler
Tom KnowltonWeb developer

Author

Commented:
After looking at my screenshots of my debugger / watch window ---   do you still think the dynamic keyword is the issue?
Most Valuable Expert 2012
Top Expert 2008

Commented:
I see the event reference, and the event handler method reference, so I don't see anything that explains why the event is not raised.

protected void Button1_Click(object sender, EventArgs e)
        {
            if (ButtonClickedEvent != null)
            {
                ButtonClickedEvent("some message");
            }         
        }

Open in new window


Are you reaching the line that triggers the ButtonClickEvent?  In other words, is ButtonClickedEvent delegate not null?
Tom KnowltonWeb developer

Author

Commented:
I am a bit fuzzy on how the AddEventHandler call is preserved across a postback...

If you have to create the controls EVERY TIME a postback happens...then how does an event ever manage to fire and do anything?
Tom KnowltonWeb developer

Author

Commented:
Are you reaching the line that triggers the ButtonClickEvent?  In other words, is ButtonClickedEvent delegate not null?

=================================

I am happy to see you ask this.....for this has been my question as well!!!!

I set a breakpoint on the if(ButtonClickedEvent !- null) and the code execution NEVER stops there in the debugger
Most Valuable Expert 2012
Top Expert 2008

Commented:
This sounds like a good time to review the page life cycle, since if you don't re-add dynamic controls to the page at the right time in the cycle, then event handlers will not be re-attached correctly when the page posts back.
Tom KnowltonWeb developer

Author

Commented:
If it helps, here is me running the code and clicking the button on the control

 knowlton-487649.flv
Tom KnowltonWeb developer

Author

Commented:
This sounds like a good time to review the page life cycle, since if you don't re-add dynamic controls to the page at the right time in the cycle, then event handlers will not be re-attached correctly when the page posts back.

==================================

For 500 points, I would like some help with this please.  What are your recommendations?

You have seen my code...what changes would I make?
Most Valuable Expert 2012
Top Expert 2008

Commented:
I try to help where I can.

1) I see DisplayLayout(layout); in the Page_Load event.

2) In the DisplayLayout method, you are loading the user control:

Control c = LoadControl("~/UserControls/" + settingsarray[0] + ".ascx");

3) Take a look at the page life cycle:

ASP.NET Page Life Cycle Overview
http://msdn.microsoft.com/en-us/library/ms178472.aspx 

Page Life Cycle
Tom KnowltonWeb developer

Author

Commented:
Where did you get that image?

Is that some sort of tool you are using?
Most Valuable Expert 2012
Top Expert 2008

Commented:
Snagit by TechSmith
http://www.techsmith.com/snagit/
Tom KnowltonWeb developer

Author

Commented:
No...not the screencapture....

The image itself....is that a screenshot of a tool you were using?
Most Valuable Expert 2012
Top Expert 2008

Commented:
Scroll down this page to see the image

ASP.NET Page Life Cycle Overview
http://msdn.microsoft.com/en-us/library/ms178472.aspx 
Tom KnowltonWeb developer

Author

Commented:
Here is a complete test project so you can:

-follow along with what I am trying
-make recommendations based on what you observe while debugging

It seems to me this should help you help me solve this.

Here is the link to the download page:

http://ube.dev.campuswebstore.mobi/dloadfile.htm
Most Valuable Expert 2012
Top Expert 2008

Commented:
I would try moving the DisplayLayout call to the Page_Init method.
Tom KnowltonWeb developer

Author

Commented:
With all due respect, just throwing a bunch of stuff at me about page life-cycle is not going to help me solve this, Bob.  Help me fix this first....then I think it will be obvious to me how the page life cycle ties in.

I have tried reloading the controls in page init in the past and I would get complaints about modifying the collection:


 //The control collection cannot be modified during
        //DataBind,
        //Init,
        //Load,
        //PreRender or
        //Unload phases.


So I am at a loss as to how a better understanding of the page lifecycle will help me.
Tom KnowltonWeb developer

Author

Commented:
>>>>>>>I would try moving the DisplayLayout call to the Page_Init method.

==============================

Tried it.......the event still does not fire




protected void Page_Init(object sender, System.EventArgs e)
        {
            string layout = "";

            if (Request.QueryString["layout"] != null)
            {
                layout = Request.QueryString["layout"] as string;
            }
            else
            {
                layout = "default";
            }


            if (!Page.IsPostBack)
            {
                GlobalMethods.InitControlList();
                LoadControls(layout);              
            }
            
        }

Open in new window

Tom KnowltonWeb developer

Author

Commented:
In the meantime....I have begun reading the article.


I will TRY to understand the page life-cycle better....but right now I am in GET IT DONE mode.....I would rather get this working...then worry about how and why later on!!!!!!!


From the first paragraph:

When an ASP.NET page runs, the page goes through a life cycle in which it performs a series of processing steps. These include initialization, instantiating controls, restoring and maintaining state, running event handler code, and rendering. It is important for you to understand the page life cycle so that you can write code at the appropriate life-cycle stage for the effect you intend.

This descibes my current dilemna pretty well....so yes, I see the value in understanding the page life-cycle better.  Touche!!!!
Tom KnowltonWeb developer

Author

Commented:
The article makes sense to me, but I am not sure how dynamically added controls work, since they seem to be a bit different from registered controls:

If controls are created dynamically at run time or declaratively within templates of data-bound controls, their events are initially not synchronized with those of other controls on the page. For example, for a control that is added at run time, the Init and Load events might occur much later in the page life cycle than the same events for controls created declaratively. Therefore, from the time that they are instantiated, dynamically added controls and controls in templates raise their events one after the other until they have caught up to the event during which it was added to the Controls collection.


I don't really understand this.....I really need some help to get this working...please!
Most Valuable Expert 2012
Top Expert 2008

Commented:
I really don't like the complexity that comes with dynamic controls, and I certainly don't like it with user controls (unlike custom web controls).

Try this:

Befuddling Error: "The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases."
http://iformattable.blogspot.com/2007/07/befuddling-error-control-collection.html

My understanding is that you can modify down, not up, in the controls hierarchy. In other words, if you want to add a control dynamically to your parent, the parent needs to have a place to put it. The solution is to add a PlaceHolder control to the parent, then add your controls dynamically to that PlaceHolder.

Tom KnowltonWeb developer

Author

Commented:
I've not been using a placeholder, I've been using a div.  Does it matter?
Tom KnowltonWeb developer

Author

Commented:
I really don't like the complexity that comes with dynamic controls, and I certainly don't like it with user controls (unlike custom web controls).

======================

I've gone to great lengths to make my little demo as UN complex as possible:

http://ube.dev.campuswebstore.mobi/dloadfile.htm

Perhaps you should tinker with it and see if anything jumps out at you!!!!
Most Valuable Expert 2012
Top Expert 2008

Commented:
I would try the PlaceHolder, since it is a different beast than a <div>.  I will not have much time to download, and play with anything right now, as I am working on a production issue of my own.
Tom KnowltonWeb developer

Author

Commented:
>>>>I would try the PlaceHolder, since it is a different beast than a <div>.  

===========================

I will!!!!!!


>>>>>>I will not have much time to download, and play with anything right now, as I am working on a production issue of my own.

==================

I sympathize!
Tom KnowltonWeb developer

Author

Commented:
I wish I could say that using a Placeholder control to house the dynamic controls made a difference, but it made no difference.
Most Valuable Expert 2012
Top Expert 2008

Commented:
I would like to assume that you put the PlaceHolder onto the parent page that loads the control, but I really should check...
Tom KnowltonWeb developer

Author

Commented:
Here is the markup for the parent page:


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication2._Default" %>

<%@ Register src="TestRaiseEvent.ascx" tagname="TestRaiseEvent" tagprefix="uc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
<uc1:TestRaiseEvent ID="TestRaiseEvent77" runat="server" />
<br />
<br />
<br />
<br />

<asp:PlaceHolder ID="sitemanagercontrolsdiv" runat="server">
</asp:PlaceHolder>

   
    </form>
</body>
</html>

Open in new window

Tom KnowltonWeb developer

Author

Commented:
I would like to assume that you put the PlaceHolder onto the parent page that loads the control, but I really should check...
==============================

Wow, you really don't like me much, do you?

LOL
Most Valuable Expert 2012
Top Expert 2008

Commented:
I really should check every time I want to make an assumption, disregarding any personalities...and, you modified the code to add to load into the PlaceHolder, and not the page...
Tom KnowltonWeb developer

Author

Commented:
>>>>I really should check every time I want to make an assumption, disregarding any personalities...

Um...  "okay"   :)


One way to avoid having to guess is to provide an example of the markup (like I just did) so that there is no confusion as to where the placeholder tag is located.  :)
Most Valuable Expert 2012
Top Expert 2008

Commented:
I bet you didn't change anything, other than the type:

GlobalMethods.divlayoutgencontrols.Add(c);
Tom KnowltonWeb developer

Author

Commented:
I bet you didn't change anything, other than the type:

GlobalMethods.divlayoutgencontrols.Add(c);

=============================================


GlobalMethods.divlayoutgencontrols.Add(c);

is a List<> of controls.  It was my approach to preserve the controls across a postback.


I am not sure I am understanding your concern.
Most Valuable Expert 2012
Top Expert 2008

Commented:
How is the user control added to the page or PlaceHolder?
Tom KnowltonWeb developer

Author

Commented:
>>>How is the user control added to the page or PlaceHolder?

It is done here:


GlobalMethods.divlayoutgencontrols.Add(c);

at the bottom of LoadControls.
Tom KnowltonWeb developer

Author

Commented:
Actually....that was adding to the global static List< > of controls.

I apologize.


It is added to the placeholder here:

  private void AddControlsFromList()
        {
            sitemanagercontrolsdiv.Controls.Clear();

            try
            {
                if (GlobalMethods.divlayoutgencontrols != null)
                {
                    foreach (Control c in GlobalMethods.divlayoutgencontrols)
                    {
                        sitemanagercontrolsdiv.Controls.Add(c);
                    }
                }
            }
            catch (Exception eee)
            {
                string a = eee.Message;
            }
        }





Keep in mind that my SMALL demo is now different than my original web page code I first posted.


It is the DEMO that I have been making changes to.
Tom KnowltonWeb developer

Author

Commented:
sitemanagercontrolsdiv    IS    a placeholder, even though the name is the same as the old <div>
Most Valuable Expert 2012
Top Expert 2008

Commented:
Where is "AddControlsFromList" called from?  You are clearing the PlaceHolder control, and then re-adding, and I don't believe that is necessary.

sitemanagercontrolsdiv.Controls.Clear();
Tom KnowltonWeb developer

Author

Commented:
It is called in Page Load and Page Load Complete.

If I don't do this, the control will disappear (the one that is added dynamically) on postback.

    protected void Page_Load(object sender, EventArgs e)
        {            
            Master.CalcCartTotal();
            Master.ShowCat();
            Master.DisplayUser();

            AddControlsFromList();
        }


        //The control collection cannot be modified during 
        //DataBind, 
        //Init, 
        //Load, 
        //PreRender or 
        //Unload phases.
        
        protected void Page_LoadComplete(object sender, EventArgs e)
        {
            AddControlsFromList();
        }

Open in new window

Most Valuable Expert 2012
Top Expert 2008

Commented:
Why is it called from 2 methods?
Tom KnowltonWeb developer

Author

Commented:
>>>>Why is it called from 2 methods?

I don't know.

I commented out the one in Page_LoadComplete and it made no difference.  The dynamic control still shows up after a postback.

So...I guess the second call was not needed.  


BUT ---  the event still does not fire after making that change.
Most Valuable Expert 2012
Top Expert 2008
Commented:
This worked for me:

Default.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <asp:PlaceHolder ID="PlaceHolder1" runat="server" />
  <asp:Label ID="Label1" runat="server" ForeColor="Red" Font-Names="Verdana" />
  </form>
</body>
</html>

Open in new window


Default.aspx.cs:

    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            dynamic control = this.LoadControl("WebUserControl1.ascx");
            control.Test += new EventHandler(control_Test);
            this.PlaceHolder1.Controls.Add(control);
        }

        void control_Test(object sender, EventArgs e)
        {
            this.Label1.Text = "Button clicked";
        }

    }

Open in new window


WebUserControl1.ascx:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="WebApplication1.WebUserControl1" %>
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />

Open in new window


WebUserControl1.ascx.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication1
{
    public partial class WebUserControl1 : System.Web.UI.UserControl
    {
        public event EventHandler Test;

        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            if (this.Test != null)
                this.Test(this, EventArgs.Empty);
        }
    }
}

Open in new window




Snapshot.png
Most Valuable Expert 2012
Top Expert 2008

Commented:
In order to use the "dynamic" keyword, I needed to add a reference to the Microsoft.CSharp.dll to my web application project.
Tom KnowltonWeb developer

Author

Commented:
In order to use the "dynamic" keyword, I needed to add a reference to the Microsoft.CSharp.dll to my web application project.

============================

Good work!!   I will check this out in a few minutes.


Now -- I don't think I really spelled this out.....and if I need to ask another question on this topic I can.....but the whole KEY to this is Reflection.  I am not sure I saw any reflection being used in your solution.


I want to be able to bind events to handlers ... but in my parent I will have pre-made handlers that will match the events being raised.  However, I will not know which events the control has until runtime.


I am trying to make a more flexible dynamic app than what most developers code for.


I will see how workable your solution is with these requirements and report back here.


Thank you,


Tom
Most Valuable Expert 2012
Top Expert 2008

Commented:
I saw that you were looping through the events, looking for event names like NameAvailabilityEvent or ButtonClickedEvent, which sounded like it lends itself to dynamic, because you know that you are looking for a specific event.  
Tom KnowltonWeb developer

Author

Commented:
I am having a difficult time getting the project to run.

I have included references to Microsoft.CSharp.dll and it still will not compile
Tom KnowltonWeb developer

Author

Commented:
Here is what I get at compile time:

 sys core
Tom KnowltonWeb developer

Author

Commented:
Any ideas?
Most Valuable Expert 2012
Top Expert 2008

Commented:
I would make sure that the project is configured for 4.0 framework.


Snapshot.png
Tom KnowltonWeb developer

Author

Commented:
>>>>I would make sure that the project is configured for 4.0 framework.

=========================


That was what I needed.  I was configured for 3.5.


So....that got your demo working.


But I am still faced with the problem of getting it to work with Reflection.

Should I ask about Reflection in another question, or keep this question open?

Are you able to help with Reflection questions?
Tom KnowltonWeb developer
Commented:
Well, I wish I could say I figured this out on my own, but my brother-in-law emailed me last night and was able to provide the solution:

In his words:


"Got it working, there were three changes.

1)      Dynamic controls must be recreated by you every time, even on IsPostBack().  You were making the call to add them (twice), but they were querying the divlayoutgencontrols object, which was only initialized on !IsPostBack.

2)      You don’t want to change the control ID, the control ID is used to assign the event, so by giving it a newGuid each time it won’t connect the event from the last new guid to the new new guid

3)      eventName == “ButtonClickedEvent” àeventName == “ButtonClickEvent”


So your event handler was working just fine, it was just that the actual event you were listening for was never called.  Now that it is your listener is triggered."


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Reflection;

namespace WebApplication2
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_PreInit(object sender, System.EventArgs e)
        {
            string layout = "";

            if (Request.QueryString["layout"] != null)
            {
                layout = Request.QueryString["layout"] as string;
            }
            else
            {
                layout = "default";
            }


            //if (!Page.IsPostBack) {
            GlobalMethods.InitControlList();
            LoadControls(layout);
            //}

        }

        private void AddControlsFromList()
        {

            sitemanagercontrolsdiv.Controls.Clear();

            try
            {
                if (GlobalMethods.divlayoutgencontrols != null)
                {
                    foreach (Control c in GlobalMethods.divlayoutgencontrols)
                    {
                        sitemanagercontrolsdiv.Controls.Add(c);
                    }
                }
            }
            catch (Exception eee)
            {
                string a = eee.Message;
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            AddControlsFromList();
        }


        //The control collection cannot be modified during 
        //DataBind, 
        //Init, 
        //Load, 
        //PreRender or 
        //Unload phases.

        protected void Page_LoadComplete(object sender, EventArgs e)
        {
            //AddControlsFromList();
        }

        private void LoadControls(string layout)
        {
            Control c = LoadControl("~/TestRaiseEvent.ascx");

            Type controlType = c.GetType();

            BindingFlags myBindingFlags = BindingFlags.Instance | BindingFlags.Public;
            EventInfo[] myEvents = controlType.GetEvents(myBindingFlags);

            for (int j = 0; j < myEvents.Count(); j++)
            {
                string eventName = myEvents.ElementAt(j).Name;
                if (eventName == "ButtonClickEvent")
                {
                    MethodInfo handler = typeof(_Default).GetMethod("SomeHandler");
                    Delegate del = Delegate.CreateDelegate(myEvents.ElementAt(j).EventHandlerType, this, handler);
                    myEvents.ElementAt(j).AddEventHandler(c, del);
                }
            }

            c.Visible = true;
            //string tempGUID = Guid.NewGuid().ToString();
            //c.ID = "TestRaiseEvent" + tempGUID + "1";
            c.ID = "TestRaiseEvent1";

            GlobalMethods.divlayoutgencontrols.Add(c);
        }


        public void SomeHandler(string message)
        {
            // do something
        }
    }
}

Open in new window

Tom KnowltonWeb developer

Author

Commented:
Thank you for your help on this Bob.  You were the only one who showed up here and gave it a try.
Most Valuable Expert 2012
Top Expert 2008

Commented:
Here is the equivalent reflection code (tested):

        protected void Page_Load(object sender, EventArgs e)
        {
            Control control = this.LoadControl("WebUserControl1.ascx");

            Type type = typeof(_Default);

            EventInfo eventInfo = control.GetType().GetEvent("Test");

            MethodInfo methodInfo = type.GetMethod("Control_Test", 
                BindingFlags.NonPublic | BindingFlags.Instance);

            Delegate eventDelegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo);
            
            eventInfo.AddEventHandler(control, eventDelegate);                       
            
            this.PlaceHolder1.Controls.Add(control);
        }

        private void Control_Test(object sender, EventArgs e)
        {
            this.Label1.Text = "Button clicked";
        }

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial