Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

Composite Control - Events in Child aren't Bubbled

Posted on 2006-04-19
5
Medium Priority
?
1,489 Views
Last Modified: 2008-02-07
Hello,

I am currently developing a tabbed UI control (with a tab for each section of the page).  My tab control consists of a main class (TabStrip) which uses composite bulding, CreateChildControls(), to add the individual Tab objects to the TabStrip.  The Tab objects contain LinkButtons which fire Command events, passing which tab was clicked.  

I have not been able to capture any fired events from the Tab objects, so I've simplified the problem down to the most basic Parent/Child composite control, shown in the code below.  What I need it to get this simplified version working and I'll then use the same principles to fix my production project.  In the below code, there is a parent control (TestParent) which uses composition to add a single child control (TestChild).  The Child has a single control, a LinkButton which fires a Command event.  This command event needs to be bubbled up to the TestParent, which is used in pages.  Currently, the LinkButton appears on the page correctly, with a link and causes a postback, but in debugging/running, I can never hit breakpoints at tabLink_Command() or OnBubbleEvent(), or in my code-behind using standard sytax (TestObj.Command += new CommandEventHandler(TestObj_Command);).

I have reviewed and similar PAQs, as well as posts on MSDN and Google for some clues, but so far I have found none.  I will award full points to anyone who can get my simplified TestParent/TestChild code working.

Thanks!
Ryan

/* ********************************
     PARENT CONTROL
*********************************/
using System;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Cfs.Web.Controls.Server
{
    public class TestParent : Control, INamingContainer {
        public event CommandEventHandler Command;
        public event EventHandler Click;

        protected override void CreateChildControls() {
            Controls.Add( new TestChild() );
        }

        protected override bool OnBubbleEvent(object sender, EventArgs e) {
            if (Command != null && e is CommandEventArgs) {
                Command(this, (CommandEventArgs)e);
            }
            else {
                Click(this, e);
            }
            return false;
        }
    }
}

/* ********************************
     CHILD CONTROL
*********************************/
using System;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Cfs.Web.Controls.Server {
    public class TestChild : WebControl {

        protected override void Render(HtmlTextWriter writer) {

            LinkButton tabLink = new LinkButton();
            tabLink.Text = "Text";
            tabLink.CommandArgument = "CmdArg";
            tabLink.CommandName = "CmdName";
            tabLink.Command += new CommandEventHandler(tabLink_Command);
            Controls.Add(tabLink);
            base.Render(writer);
        }

        protected void tabLink_Command(object sender, CommandEventArgs e) {
            this.RaiseBubbleEvent(this, e);
        }
    }
}
0
Comment
Question by:rmariotti
  • 4
5 Comments
 

Author Comment

by:rmariotti
ID: 16489042
Just for some additional details that might get things moving in the right direction:

Look to the bottom, "Detecting and Bubbling Up Events".  Looks like this is identical to what I'm trying to accomplish.
* http://msdn.microsoft.com/asp.net/community/authors/scottmitchell/default.aspx?pull=/library/en-us/dnaspp/html/databoundtemplatedcontrols.asp

Another similar case, but in VB
* http://aspnet.4guysfromrolla.com/articles/051105-1.aspx

Reference, the RaiseBubbleEvent()
http://msdn2.microsoft.com/en-US/library/system.web.ui.control.raisebubbleevent(VS.80).aspx

Cheers,
Ryan
0
 
LVL 33

Accepted Solution

by:
raterus earned 500 total points
ID: 16490569
I'm looking at your Child control, and I don't think it's created properly.  You're using the Render method, which isn't for use in composite controls.  You should be using CreateChildControls in the child control if you are going to add a LinkButton like that.  Ultimately, I'd say your event isn't firing because the Render event comes way after where it should be created if you are going to catch an event on it.  I'd change it to something like this,

/* ********************************
     CHILD CONTROL
*********************************/
using System;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Cfs.Web.Controls.Server {
    public class TestChild : WebControl {

        protected override void CreateChildControls() {

            LinkButton tabLink = new LinkButton();
            tabLink.Text = "Text";
            tabLink.CommandArgument = "CmdArg";
            tabLink.CommandName = "CmdName";
            tabLink.Command += new CommandEventHandler(tabLink_Command);
            Controls.Add(tabLink);
        }

        protected void tabLink_Command(object sender, CommandEventArgs e) {
            this.RaiseBubbleEvent(this, e);
        }
    }
}
0
 

Author Comment

by:rmariotti
ID: 16493054
Solved...sort of.  

I began reviewing some documentation on this subject in my Wrox Professional ASP.NET Server Controls - Building Custom Controls with C#...and found a note reminding developers that all composite controls must implement INamingContainer to wire events back to their child controls.

I moved the building of my code from Render() to CreateChildControls() and made the parent control implement INamingContainter, and the event fired successfully.  For anyone else reading this, note that it was the missing INamingContainter that was causing my events to be lost (in addition to the fact that events can't be wired at the Render() or OnPreRender() methods...thanks to raterus on that point).

SO: my issue now is that the OnBubbleEvent() of the TestParent control fires TWICE.  The updated code is shown below.  Only OnBubbleEvent() fires twice; the OnCommand() method in the TestChild control only fires once.  Any ideas?

- Ryan


/* ********************************
     PARENT CONTROL - Updated
*********************************/
using System;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Cfs.Web.Controls.Server
{
    public class TestParent : Control, INamingContainer {
        public event CommandEventHandler TabClicked;

        protected override void CreateChildControls() {
            Controls.Add(new TestChild());
        }

        protected override bool OnBubbleEvent(object sender, EventArgs e) {
            if (TabClicked != null && e is CommandEventArgs) {
                TabClicked(this, (CommandEventArgs)e);
                return true;
            }
            else {
                return false;
            }
        }
    }
}

/* ********************************
     CHILD CONTROL - Updated
*********************************/
using System;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Cfs.Web.Controls.Server {

    public class TestChild : Control, INamingContainer {

        protected override void CreateChildControls(){
            LinkButton tabLink = new LinkButton();
            tabLink.ID = "TabLink";
            tabLink.Text = "Text";
            tabLink.CommandArgument = "CmdArg";
            tabLink.CommandName = "CmdName";
            tabLink.Command += new CommandEventHandler(OnCommand);
            Controls.Add(tabLink);
            base.CreateChildControls();
        }

        protected virtual void OnCommand(object sender, CommandEventArgs e){
            RaiseBubbleEvent(this, e);    
        }
    }
}
0
 

Author Comment

by:rmariotti
ID: 16493196
I'm not sure if this is the appropriate way to do this, but I found a way to expose the Child control's event and have it fire only once.  But it requires defining a public event delegate in the parent as WELL AS the child.

IN TestChild control:
        protected override void CreateChildControls() {
            ...
            myLinkButton.Command += new CommandEventHandler(OnCommand);    // The TabEvent from the LinkButton is wired up
            ...
        }

        protected virtual void OnCommand(object sender, CommandEventArgs e) {  
            TabClicked(this, e);
        }


Then in the parent, as I add each instance of the TestChild control, I have to wire up the event of the TestChild object which was exposed in the above code.  It looks like this in the Parent control:

IN Parent Control:
        public event CommandEventHandler TabClicked;   // This is the delegate referenced by the page

        protected override void CreateChildControls()
        {
            ...
            TestChild child1 = new TestChild();
            child1.TabClicked += new CommandEventHandler(OnTabClicked);    // The TabEvent from the CONTROL is wired up
            ...
        }

        protected void OnTabClicked(object sender, CommandEventArgs e) {
            TabClicked(this, e);
        }


I'd still like to figure out how to get the bubbling working, since that seems to be the correct way to handle eventing from within Composite controls.
Ryan
0
 

Author Comment

by:rmariotti
ID: 16493563
Well, as sometimes happens here on Experts, the poster figures out the solution along the way.  I figured out how to Bubble events properly.  

The concept that I was missing was the fact that both the TestParent and TestChild controls are composite controls which BOTH bubble events.  The TestChild itself bubbles up events thrown by it's enclosing LinkButtons.  So I removed explicit event registrations/wireups on the TestChild LinkButton, and added a catch-all OnBubbleEvent which just checks for a CommandEventArg and passes the event up by calling RaiseBubbleEvent().

The parent object does the same, listening for all events from it's children (TestChild controls), then calls it's public events which pages/etc. can reference.  Since raterus was the sole responder, I'll allocate the points regardless, and hope that my post will be useful to other developers.  Again, the key was the double-nested nature of my control hierarchy...both parent and child needed to bubble events since they both contained child controls.

Ryan

<h3>F I N A L    C O D E</h3>

/* ********************************
     PARENT CONTROL - Final
*********************************/
using System;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Cfs.Web.Controls.Server
{
    public class TestParent : Control, INamingContainer
      {
        public event CommandEventHandler TabClicked;

        protected override void CreateChildControls()
        {
            Controls.Add(new TestChild());
        }

        protected override bool OnBubbleEvent(object sender, EventArgs e)
        {
            if (TabClicked != null && e is CommandEventArgs)
            {
                TabClicked(this, (CommandEventArgs)e);
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}



/* ********************************
     CHILD CONTROL - Final
*********************************/
using System;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Cfs.Web.Controls.Server
{
    public class TestChild : Control, INamingContainer
    {

        protected override void CreateChildControls()
        {
            LinkButton tabLink = new LinkButton();
            tabLink.ID = "TabLink";
            tabLink.Text = "Text";
            tabLink.CommandArgument = CommandArg;
            tabLink.CommandName = "CmdName";
            Controls.Add(tabLink);

            base.CreateChildControls();
        }

        protected override bool OnBubbleEvent(object sender, EventArgs e)
        {
            if (e is CommandEventArgs)
            {
                RaiseBubbleEvent(this, e);    
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I have developed many web applications with asp & asp.net and to add and use a dropdownlist was always a very simple task, but with the new asp.net, setting the value is a bit tricky and its not similar to the old traditional method. So in this a…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
this video summaries big data hadoop online training demo (http://onlineitguru.com/big-data-hadoop-online-training-placement.html) , and covers basics in big data hadoop .
Integration Management Part 2
Suggested Courses
Course of the Month10 days, 11 hours left to enroll

572 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question