Reputation: 28867
Part of the series of controls I am working on obviously involves me lumping some of them together in to composites. I am rapidly starting to learn that this takes consideration (this is all new to me!) :)
I basically have a StyledWindow
control, which is essentially a glorified Panel
with ability to do other bits (like add borders etc).
Here is the code that instantiates the child controls within it. Up till this point it seems to have been working correctly with mundane static controls:
protected override void CreateChildControls()
{
_panel = new Panel();
if (_editable != null)
_editable.InstantiateIn(_panel);
_regions = new List<IAttributeAccessor>();
_regions.Add(_panel);
}
The problems came today when I tried nesting a more complex control within it. This control uses a reference to the page since it injects JavaScript in to make it a bit more snappy and responsive (the RegisterClientScriptBlock
is the only reason I need the page ref).
Now, this was causing "object null" errors, but I localized this down to the render method, which was of course trying to call the method against the [null] Page
object.
What's confusing me is that the control works fine as a standalone, but when placed in the StyledWindow
it all goes horribly wrong!
So, it looks like I am missing something in either my StyledWindow
or ChildControl
. Any ideas?
As Brad Wilson quite rightly pointed out, you do not see the controls being added to the Controls
collection. This is what the _panel
is for, this was there to handle that for me, basically then override Controls
(I got this from a guide somewhere):
Panel _panel; // Sub-Control to store the "Content".
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return _panel.Controls;
}
}
I hope that helps clarify things. Apologies.
Right, I have been doing some playing with the control, placing one within the composite, and one outside. I then got the status of Page at event major event in the control Lifecycle and rendered it to the page.
The standalone is working fine and the page is inited as expected. However, the one nested in the Composite is different. It's OnLoad
event is not being fired at all! So I am guessing Brad is probably right in that I am not setting up the control hierarchy correctly, can anyone offer some advice as to what I am missing? Is the Panel method not enough? (well, it obviously isn't is it?!) :D
Thanks for your help guys, appreciated :)
Upvotes: 4
Views: 3496
Reputation: 28867
Right, I was determined to get this cracked today! Here were my thoughts:
Panel
was a bit of a hack, so I should remove it and find out how it is really done.MyCtl.Controls[0].Controls
to access the controls added to the composite.So, I got searching and hit MSDN, this artcle was REALLY helpful (i.e. like almost copy 'n' paste, and explained well - something MSDN is traditionally bad at). Nice!
So, I ripped out the use of Panel
and pretty much followed the artcle and took it as gospel, making notes as I went.
Here's what I have now:
For example, here is the ASPX markup for a templated control:
<cc1:TemplatedControl ID="MyCtl" runat="server">
<Template>
<!-- Templated Content Goes Here -->
</Template>
</cc1:TemplatedControl>
public class DummyWebControl : WebControl
{
// Acts as the surrogate for the templated controls.
// This is essentially the "interface" for the templated data.
}
In TemplateControl.cs...
ITemplate _template;
// Surrogate to hold the controls instantiated from
// within the template.
DummyWebControl _owner;
protected override void CreateChildControls()
{
// Note we are calling base.Controls here
// (you will see why in a min).
base.Controls.Clear();
_owner = new DummyWebControl();
// Load the Template Content
ITemplate template = _template;
if (template == null)
template = new StyledWindowDefaultTemplate();
template.InstantiateIn(_owner);
base.Controls.Add(_owner);
ChildControlsCreated = true;
}
Then, to provide easy access to the Controls of the [Surrogate] Object:
(this is why we needed to clear/add to the base.Controls)
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return _owner.Controls;
}
}
And that is pretty much it, easy when you know how! :)
Next: Design Time Region Support!
Upvotes: 1
Reputation: 28867
Right, I got playing and I figured that there was something wrong with my control instantiation, since Longhorn was right, I should be able to create script references at OnLoad
(and I couldn't), and Brad was right in that I need to ensure my Controls
hierarchy was maintained by adding to the Controls
collection of the composite.
So, I had two things here:
Controls
property accessor for the composite to return this Panel
's Controls collection since I dont want to have to go ctl.Controls[0].Controls[0]
to get to the actual control I want. I have removed this, but I need to get this sorted.Panel
to the Controls
collection, I have now done this.So, it now works, however, how do I get the Controls
property for the composite to return the items in the Panel
, rather than the Panel
itself?
Upvotes: 0
Reputation: 73311
I have always put the JavaScript calls on the OnLoad Function. Such as below.
protected override void OnLoad(EventArgs e)
{
// Do something to get the script
string script = GetScript();
this.Page.ClientScript.RegisterClientScriptBlock(this.Page.GetType(), "SomeJavaScriptName", script);
// Could also use this function to determine if the script has been register. i.e. more than 1 of the controls exists
this.Page.ClientScript.IsClientScriptBlockRegistered("SomeJavaScriptName");
base.OnLoad(e);
}
If you still want to do the render, then you can just write the script in the response. Which is what the RegisterScriptBlock does, it just puts the script inline on the page.
Upvotes: 1
Reputation: 70586
I don't see you adding your controls to the Controls collection anywhere, which would explain why they can't access the Page (since they've never been officially placed on the page).
Upvotes: 2