Container data values aren't coming through using a Templated User Control - asp.net

I have created a Templated UserControl. I don't want to bloat this post by posting all the code I used to create the control but suffice to say that I am fairly certain that the code is correct. I will post a few snippets to show that I do know the proper way to do this. First is my ITemplate implementation:
private ITemplate _NutritionLabelTemplate = null;
[TemplateContainer(typeof(NutritionLabelContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate NutritionLabelTemplate
{
get { return _NutritionLabelTemplate; }
set { _NutritionLabelTemplate = value; }
}
Then the implementation of my INamingContainer Interface:
public class NutritionLabelContainer : Control, INamingContainer
{
}
My template markup has a Placeholder control named "phNutritionLabel_Template" and cutting through the other blah blah blah, I've got something like:
phNutritionLabel_Template.Controls.Clear();
if (NutritionLabelTemplate != null)
{
NutritionLabelContainer nContainer = new NutritionLabelContainer();
nContainer.calcium = calcium;
nContainer.calories = calories;
NutritionLabelTemplate.InstantiateIn(nContainer);
phNutritionLabel_Template.Controls.Add(nContainer);
}
Afterward, I add this templated UserControl to my webpage and to test it I add the following code:
<uc1:NutritionalLabel_Template ID="NutritionalLabel_Template1" runat="server"
servingSize="28"
calories="46">
<NutritionLabelTemplate>
<h1>Template Calories</h1>
<span style="font-size:large; font-style:italic;"></span>
<asp:Label ID="Label1" runat="server">
<%#Container.calories %>
</asp:Label>
<br />
</NutritionLabelTemplate>
</uc1:NutritionalLabel_Template>
When I run the page the value of #Container.calories isn't visible, it's totally blank. I've debugged my code and stepping through it I can see that the values are clearly being set in the NamingContainer of the Templated Control and that the values are being carried over to the Placeholder via the NamingContainer.
I can also verify the values are set by placing the following code in the code-behind file of the webpage and then see the values reflected on the page. But otherwise, nothing.
Label1.Text = NutritionalLabel_Template1.calories;
I have seen this problem arise before and can't remember what the cause or resolution was. Can anyone provide any pointers?
I've only created these types of controls a few times so it's a bit new to me. Any help would be appreciated. I'm stumpped.
Thx

I never received any responses to this post so regardless of why no one could provide any clues to the problem, I figured out what the problem was and thought I'd post the solution for anyone who may potentially have the similiar issue to save you some needless frustration. ;-)
Turns out that I'd forgotten to add the DataBind() method to the Page_Load event of the default page. Per Microsoft, this method ensures a binding of data from a source to a server control and it's commonly used after retrieving a dataset through a database query. But since most controls perform data binding automatically, you typically should not need to make an explicit call to this method.
However, the method is also commonly overridden when you create a custom templated data-bound control. But in my case, I overrode the PageInit in the Templated control; not the Databind method. So apparently an explicit call should be made to the method in the calling page's Page_Load event to ensure the templated control and the data are bound in this case.

Related

ObjectDataSource methods can't refernce master page methods

So I have ObjectDataSource that has an update and delete method associated with it and the DS is conntected to a gridview. When I call the methods, I want to repopulate a dropdown in the Master page that contains the same data as the grid, So i am trying to call the methods as follows:
MyApp MasterPage = (MyApp)Page.Master;
MasterPage.getData();
Now, if I do an insert from the grid (which is handled by the RowCommand event and not the DS) this works just fine, but thru the DS i get the following error: "Object reference not set to an instance of an object."
I can see certain fields on the aspx not being available yet from a postback, but how could a master page not be available to a page that is dependent on it? Shouldn't i be able to reference it? is there some backdoor trick to this? I know I could get rid of the DS, but I want to use it cause it makes paging and sorting so easy.
Thanks
Check the order of events in the masterpage-contentpage life-cycle. You might be able to move your code to an event that fires after the master page has access to the dropdown box.
EDIT: an alternative
If you can't move the code, try to store the data in a property of the master page, then, once the dropdown has been initialised, go back to the master page and get the data.
protected object SomePropertyICreatedToStoreData {get;set;}
void GetData()
{
// get the data (this part works already)
var theData = WhereTheDataComesFrom
// bind to dropdownlist - doesn't work, so...
this.SomePropertyICreatedToStoreData = theData;
}
... then later
this.MyDropDownList.DataSource = this.SomePropertyICreatedToStoreData;
You need to add the Master directive:
<%# MasterType TypeName ="MasterPageClassName" %>
or
<%# MasterType virtualpath="~/Masters/Master1.master" %>
http://msdn.microsoft.com/en-us/library/xxwa0ff0.aspx
Update: Separated TypeName and virtualpath as pointed out in the comments.

Access html control from server side

How can i access html control from code behind in asp.net. I do not want to use "runat=server" as it is causing problems.
I have made an empty html table and from my code behind, I want to add <td> to it.
Please help.
Thanks,
Prachi
This is not possible without runat="server".
You say this was causing problems. Please say what problems it was causing.
You say you were accessing the table using getElementById? The ID of a server control changes based on what controls it's inside of. You need to get the changed ID to use:
var tab = getElementById("<%= myTable.ClientId %>");
or something to that effect.
You should use runat=server attribute if you want to access it at code behind.
But maybe you can generate some javascript code that adds the td/tr to your table at codebehind. and you can register it yo your page to run at startup.
runat="server"
to the table and the tr elements, there is no way the code behind can access them (as this tells the frame work to expose the elements to the server).
What are you actually trying to do? Have you looked at either DataGrid, GridView or simple Repeater control? These allow you to define a table structure and dynamically add controls to it (usually through data binding, although there are ways to add items to thier ItemCollections).
one way that you could do this is to add some inline scripting and a public string variable. in your code behind, make a public class-level variable:
public String myColumns
then in your Page_Load event, create your HTML as a string and save it in your myColumns variable:
protected void Page_Load() {
//do stuff
myColumns = someStringWithTDTagsInIt
}
Then in your .aspx page, do the following:
<table id=maintable"><tr><%=myColumns %></tr></table>
It should render your HTML as you wanted it to in your Page_Load event (or whatever event you want, if you want it to load on a button click or something) without using a "runat=server" tag.
Please note that this is not the recommended way to achieve this (controls are typically more efficient) but this should be a valid solution to the question you posed.
simple example.
just you place the runat="server" then you can access .
html controls are cannot access at server side. if you place runat server then it access in the server.

ASP.NET: Pass value from User Control to page?

I am creating a user control in ASP.NET (using VB) that uses the autocomplete ajax control on a textbox to get a value. Then I want the page to post back and run some code according to whatever value is passed to it from this control. Problem is, I'm not exactly sure how to do this. I'm sure it's easy and I should know, but I don't.
Thanks in advance!
In your user control expose a property for the value
Public Property SomeValue() As String
Get
Return textbox1.Text
End Get
End Property
Then in your aspx page load, just reference the user control's value.
userControl1.SomeValue
Edit, I just tried changing my syntax to vb.net, I don't actually know vb, so that syntax may or may not be right.
((NameOfPage)this.Page).VariableOnPage = this.Foobar;
In the code-behind on your user-control expose a property e.g.
public TextBox UserControlTextBox
{
return this.TextBoxInstance;
}
Then from you page just call
UserControlInstance.UserControlTextBox.Text;

Child Control Initialization in Custom Composite in ASP.NET

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?
Update
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.
Update Following Longhorn213's Answer
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 :)
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).
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.
Solved!
Right, I was determined to get this cracked today! Here were my thoughts:
I thought the use of Panel was a bit of a hack, so I should remove it and find out how it is really done.
I didn't want to have to do something like MyCtl.Controls[0].Controls to access the controls added to the composite.
I wanted the damn thing to work!
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:
I learned I was using the wrong term. I should have been calling it a Templated Control. While templated controls are technically composites, there is a distinct difference. Templated controls can define the interface for items that are added to them.
Templated controls are very powerful and actually pretty quick and easy to set up once you get your head round them!
I will play some more with the designer support to ensure I fully understand it all, then get a blog post up :)
A "Template" control is used to specify the interface for templated data.
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>
Heres the Code I Have Now
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!
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:
I had overriden the 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.
I had not added the 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?

Creating a UserControl Programmatically within a repeater?

I have a repeater that is bound to some data.
I bind to the ItemDataBound event, and I am attempting to programmatically create a UserControl:
In a nutshell:
void rptrTaskList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
CCTask task = (CCTask)e.Item.DataItem;
if (task is ExecTask)
{
ExecTaskControl foo = new ExecTaskControl();
e.Item.Controls.Add(foo);
}
}
The problem is that while the binding works, the user control is not rendered to the main page.
Eh, figured out one way to do it:
ExecTaskControl foo = (ExecTaskControl)LoadControl("tasks\\ExecTaskControl.ascx");
It seems silly to have a file depedancy like that, but maybe thats how UserControls must be done.
You could consider inverting the problem. That is add the control to the repeaters definition and the remove it if it is not needed. Not knowing the details of your app this might be a tremendous waste of time but it might just work out in the end.
If you are going to do it from a place where you don't have an instance of a page then you need to go one step further (e.g. from a webservice to return html or from a task rendering emails)
var myPage = new System.Web.UI.Page();
var myControl = (Controls.MemberRating)myPage.LoadControl("~/Controls/MemberRating.ascx");
I found this technique on Scott Guithrie's site so I assume it's the legit way to do it in .NET
I think that #Craig is on the right track depending on the details of the problem you are solving. Add it to the repeater and remove it or set Visible="false" to hide it where needed. Viewstate gets tricky with dynamically created controls/user controls, so google that or check here if you must add dynamically. The article referenced also shows an alternative way to load dynamically:
Control ctrl=this.LoadControl(Request.ApplicationPath +"/Controls/" +ControlName);

Resources