I give in...

  • Thread starter Thread starter nobody
  • Start date Start date
N

nobody

Ok, I give in (not up yet... just in). Can someone *PLEASE* point me to a
good "how to" or tutorial on designing composite WebControls? I obviously
have no clue what I'm doing. :( I think my problem might be that I'm a
"windows forms" person and this ASP thing is a different creature when it
comes to state and when/how you must persist it.

I'm adding a RadioButtonList to my WebControl
(me.conrols.add(m_RadioButtonList). I have an event handler for the
SelectedIndexChanged to store the currently selected index in m_Answer and I
have a readonly property Answer to allow the aspx to get the answer from my
control at any time (like when the user clicks a submit button). On the
aspx page, I have a button that just sets a label on the page to my
control's .Answer property. It's blank, even when I've made a selection in
the RadioButtonList. My SelectedIndexChanged event handler is never being
called to set m_Answer. And also, when the button is clicked, it redraws
the page (with the blank "Answer" label) with all the options in the
RadioButtonList in my control gone.
 
Sounds like you are using VB; here's some code in C# (I'm typing this in
OE, so I'm not sure it will even compile). The "trick" here to make sure
that your "inner" (composed?) control gets created in the right spot. The
ideal place is CreateChildControls (which, given the name makes some sense).
The second "trick" is to make sure that CreateChildControls is called in the
"right" place when the page controls are being reconstructed, this is done
using the override on Controls (the other obvious place to add new controls
is OnInit -- given the name -- but that's almost always not the right place
to do it). The last little trick, is to include the INamingContainer
interface in the inheritance...

If you have problems with this, you should turn on page tracing (in your
ASPX file) and add some Page.Trace.Write statements; it will really help to
get a handle on where/when things are happening (make sure you wrap the Page
access around an IF statement to check if Page is null -- for example: if
(this.Page != null) this.Page.Trace.Warn("Hello trace world"); )

There are several good books on this:

http://www.amazon.com/exec/obidos/t...002-6234302-1442437?v=glance&s=books&n=507846

And if you are just getting started, this is a must have:

http://www.amazon.com/exec/obidos/t...002-6234302-1442437?v=glance&s=books&n=507846


public class YourCompositeControl : System.Web.UI.Control, INamingContainer
{
public override ControlCollection Controls
{
get
{
this.EnsureChildControls();
return base.Controls;
}
}

private RadioButtonList m_RadioButtonList;
protected override void CreateChildControls()
{
this.Controls.Clear();
m_RadioButtonList = new RadioButtonList();
// add buttons to your list; could be a data source/data bind thing
as well
m_RadioButtonList.SelectedIndexChanged += new
EventHandler(m_RadioButtonList_SelectedIndexChanged);
Controls.Add(m_RadioButtonList);
this.ChildControlsCreated = true;
}

string m_answer = string.Empty;
public string Answer
{
get
{
this.EnsureChildControls();
// now if you wanted to be clever you could take a look at the
m_RadioButtonList here
// and see if there's a selected index and that would keep you
from doing this selected index changed
// thing --
return m_answer;
}
}

private void m_RadioButtonList_SelectedIndexChanged(object sender,
EventArgs e)
{
RadioButtonList rbl = sender as RadioButtonList;
if (rbl != null && rbl.SelectedIndex != -1)
{
m_answer = rbl.SelectedValue;
}
}
}
 
Ok, I added the override for the Controls property, and I changed the
CreateChildControls override to actualy create the RadioButtonList (I was
doing "Private m_RadioButtonList as new WebControls.RadioButtonList"... I've
dropped the "New"), fill it in from a private ListItemCollection variable
(exposed through a property and filled in via code in the .aspx's Page_Load
event), add it to me.controls, and then set me.ChildControlsCreated = True.

I also changed the Answer Get property to call me.EnsureChildcontrols and
then Return m_RadioButtonList.SelectedValue. When I run the project, the
control shows on the web page just fine. All the choices added to
qbxQuestion (my control on the page) are there. I select one of the
choices, and click the "Submit" button. This fires a cmdSubmit.Click event
and the .aspx handles it in cmdSubmit_Click(...). This just does a
lblAnswer.Text = "Answer: " & qbxQuestion.Answer. That's the only line of
code in the cmdSubmit_Click handler. It hits an error. m_RadioButtonList
is Nothing, and m_Choices (the ListItemCollection that was filled in by the
Page_Load event) is empty. It's as if a new instance of the control was
created when the Submit button was clicked. Is this normal? Is every
interaction the user does with the server going to create a new instance of
all the controls on the page? Is there something I need to do to "persist"
the private member variables of my control?
 
Yes, you can save some of the private members using the ViewState; if your data collection is not
that big, you can try to persist in the ViewState (keep in mind that the ViewState can become quite
large, and one doesn't want to go crazy adding stuff to it).

Yes, round trips to the server, mean that the control heirarchy will almost always be recreated
each time.

I'm not exactly sure what you are doing; but event handlers, like cmdSubmit_Click are handled
early. What I think you are doing in Page_Load, a data bind to get the radio boxes, should be done
in the CreateChildControls (don't just create the RadioButtonList, actually populate it with a
datasource/databind).
 
Well, I solved my problem. It was a lack of understanding of the order of
events. I discovered that my control's CreateChildControls(...) method
(which created the inner controls and filled them in with data) was being
called *BEFORE* the Page_Load event (which passed the data to my control to
use in it's CreateChildControls method). Slight problem with the order of
things there, ya think? :)

The Page_Init(...) event fires before my control's CreateChildControls(...),
so I'm passing the data to the control there instead and it's working like a
champ now. Lesson learned. Some lessons just take longer (and more banging
of the head against the desk) then others. :)
 
Back
Top