ASP.NET Templated User Controls "Container" keyword - asp.net

I'm working with Templated User Control's. In the final markup of the control, the data is being accessed by the Container keyword. I'm using the word 'keyword' freely, because I do not understand whether this is a keyword, or where the Container word is coming from. Below is an example from my book.
//Address User Control markup
<%# Control Language="C#" AutoEventWireup="true"
CodeFile="AddressUcTemplated.ascx.cs" Inherits="AddressUcTemplated" %>
<asp:PlaceHolder runat="server"
ID="PlaceHolderAddressTemplate">
</asp:PlaceHolder>
--
//Address User Control code-behind
public partial class AddressUcTemplated :
System.Web.UI.UserControl
{
protected void Page_Init(object sender, EventArgs e)
{
//clear the controls from the placeholder
PlaceHolderAddressTemplate.Controls.Clear();
if (LayoutTemplate == null)
{
PlaceHolderAddressTemplate.Controls.Add(
new LiteralControl("No template defined."));
}
else
{
AddressUcContainer container = new
AddressUcContainer(this.Address);
this.LayoutTemplate.InstantiateIn(container);
//add the controls to the placeholder
PlaceHolderAddressTemplate.Controls.Add(container);
}
}
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateContainer(typeof(AddressUcContainer))]
public ITemplate LayoutTemplate { get; set; }
public Address Address { get; set; }
}
--
//Naming Container Class
public class AddressUcContainer : Control, INamingContainer
{
public AddressUcContainer(Address address)
{
this.Address = address;
}
public Address Address { get; set; }
}
--
//Page using the user control; the Container keyword is confusing me in the below //statement
...
<%# Register src="AddressUcTemplated.ascx" tagname="AddressUcTemplated"
tagprefix="uc1" %>
<uc1:AddressUcTemplated ID="AddressUcTemplated1"
runat="server" AddressType="Home">
<LayoutTemplate>
<h1>Edit Home Address</h1>
<table>
<tr>
<td>Address Line 1:</td>
<td>
<asp:TextBox ID="TextBoxAddress" runat="server"
Text="<%#Container.Address.AddressLine1%>"></asp:TextBox>
...

My example code is as follows:
<asp:Repeater runat="server">
<ItemTemplate><%# Container.DataItem %></ItemTemplate>
</asp:Repeater>
Intellisense states that Container is a field/variable of type RepeaterItem. The variable-part tells me that this is some special parsing, since it would most probably been a property if it was public stuff.
Anyway, my code is parsed into, amongst other, the following databinding code:
public void __DataBind__control4(object sender, EventArgs e) {
var target = (DataBoundLiteralControl)sender;
var Container = (RepeaterItem)target.BindingContainer;
target.SetDataBoundString(0, Convert.ToString(Container.DataItem, CultureInfo.CurrentCulture));
}
<%# ... %> is a DataBoundLiteralControl, and Container is the variable that's exposed to intellisense. This also shows that there's a target variable, which does not show up in intellisense, but compiles without any problems. Note that this also gives you access to everything private in the generated class, like __fileDependencies.
<%# target %> works, while <%# dummy %> doesn't. And while at it, <%# __DataBind__control4(null, null) %> creates two compilation errors, 1) "The best overloaded method match for 'System.Convert.ToString(object, System.IFormatProvider)' has some invalid arguments" and 2) "Argument 1: cannot convert from 'void' to 'object'".
This looks like a simple case of whatever is written between <%# ... %> is placed in Convert.ToString(..., CultureInfo.CurrentCulture). It's probably more advanced, involving different ControlBuilders, TemplateParsers, and an ounce of magic, but I think my abstraction works well enough to understand this.

Related

Templated RadDock control inside of a RadDockZone?

I've created a templated ASP.NET user control based on the RadDock control. However, when adding such a control to a RadDockZone causes a runtime error stating that it can only contain RadDock controls. Is there any way to solve this?
Templated user control markup
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="TemplatedDock.ascx.cs" Inherits="TemplatedDock" %>
<telerik:RadDock ID="RadDock1" runat="server" EnableAnimation="True" DockHandle="Grip" Resizable="True">
<ContentTemplate>
<asp:PlaceHolder ID="dockPlaceholder" runat="server"></asp:PlaceHolder>
</ContentTemplate>
</telerik:RadDock>
Templated user control code-behind
public partial class TemplatedDock : System.Web.UI.UserControl
{
private ITemplate _content;
[TemplateContainer(typeof(ContentContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
public ITemplate Content
{
get
{
return _content;
}
set
{
_content = value;
}
}
void Page_Init()
{
if (_content != null)
{
ContentContainer container = new ContentContainer();
_content.InstantiateIn(container);
dockPlaceholder.Controls.Add(container);
}
}
}
public class ContentContainer : Control, INamingContainer{}
}
Usage in RadDockZone
<telerik:RadDockZone ID="RadDockZone1" runat="server">
<a:TemplatedDock>
<Content>
<telerik:RadGrid ID="someGrid" runat="server"></telerik:RadGrid>
</Content>
</a:TemplatedDock>
</telerik:RadDockZone>
There is no way for this to happen. A RadDockZone must have only RadDock controls as children. Custom controls have a different type and will, therefore throw an exception.
The zone is tightly coupled with the dock to offer easy integration like drag-drop, state saving, etc., and this has its price.

Why does my ASP.NET UserControl type keep becoming inaccessible?

I've got an ASP.NET web site that includes a usercontrol that's added programmatically to an ASP.NET web form, using this code:
var control = (Deal)LoadControl("Deal.ascx");
control.ImageFile = dealNode.SelectSingleNode("thumbnail").InnerText;
control.ProductName = dealNode.SelectSingleNode("product").InnerText;
control.PriceText = dealNode.SelectSingleNode("price").InnerText;
DealList.Controls.Add(control);
The Deal control is really simple:
<%# Control Language="C#" AutoEventWireup="true" CodeFile="Deal.ascx.cs" Inherits="Deal" %>
<div class="deal">
<img id="DealImage" runat="server" class="dealimage" />
<div class="dealtext"><asp:Label ID="DealLabel" runat="server"></asp:Label></div>
<div class="dealprice"><asp:Label ID="DealPriceLabel" runat="server"></asp:Label></div>
</div>
with the code behind it:
public string ImageFile { get; set; }
public string ProductName { get; set; }
public string PriceText { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
this.DealImage.Src = "images/" + ImageFile;
this.DealLabel.Text = ProductName;
this.DealPriceLabel.Text = PriceText;
}
When I run up the web site in Visual Studio 2010, it usually works fine. However, every so often (usually after a check-in or restarting VS2010), it gives up and won't compile. I get the error:
The type or namespace name 'Deal' could not be found (are you missing a using directive or an assembly reference?)
The problem being the cast in the line:
var control = (Deal)LoadControl("Deal.ascx");
If I comment out the usercontrol's Page_Load code, recompile, then uncomment the code and recompile, everything's OK again.
Can anyone tell me what's going on?
Try adding a reference to the control inside the page markup :
<%# Reference Control="MyUserControl.ascx" %>
See here

How to access id of <asp:Hidden> control from ascx page to cs page

This is my ascx Code:
<%# Control Language="C#" AutoEventWireup="true" CodeFile="Demo.ascx.cs"
Inherits="Demo" %>
<asp:HiddenField ID="hidden" runat="server" Value="" />
And the aspx:
<%# Register TagName="Hidden" TagPrefix="CRS" Src="~/Demo.ascx" %>
<div>
<CRS:Hidden ID="hid" runat="server" />
</div>
Now How to access Hidden variable ID From ascx page to this cs page backend
Do you mean the actual ID? or the Value within the hidden field?
You can access the value using the FindControl method
HiddenField hf = (HiddenField)this.hid.FindControl("hidden");
string theValue = hf.Value;
Not sure if this is exactly what you are looking for.
Alternatively, you can declare some public properties in the UserControl in which you can access directly
In the ascx code:
public string theValue { get; set; }
In the aspx code:
string theValue = this.hid.theValue;
To access the HiddenField inside the UserControl from the asp.net web page you will need to wire up something called a Public Property.
This code should be added to the UserControl ascx.cs code behind:
public string Value
{
get { return hidden.Value; }
set { hidden.Value = value; }
}
You could then write code like this in your asp.net page:
string SomeHiddenValue = hid.Value;
hid.Value = "Its a secret!";
Note: I haven't compiled this so I am not sure if the public property name of Value will compile. I am also not sure if the second value in set { hidden.Value = value; } needs capitalising. Try changing these two values if you encounter problems.

What is wrong with my CompositeDataBoundControl?

I have a CompositeDataBoundControl defined as below:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="ReportSection.ascx.cs" Inherits="WebReports.ReportSection" %>
<div class="report-section span-24">
<h3>
<%= Title %></h3>
</div>
public partial class ReportSection : CompositeDataBoundControl
{
public string Title { get; set; }
protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
//throw new NotImplementedException();
return 1; // JUst something to avoid exceptions.
}
}
Yet when I try and load a web form containing this control, I get the following parser error:
'WebReports.ReportSection' is not allowed here because it does not extend class 'System.Web.UI.UserControl'.
This seems very odd to me, because I get the impression that CompositeDataBoundControl is intended as a base class for user controls, or is it perhaps only for use with server controls?
It is intended for server controls, not UserControls. UserControls are more similar to Pages than to other controls (both inherit from TemplatedControl).
You can see from the MSDN description for CompositeDataBoundControl that it's not really intended for user controls:
Represents the base class for a tabular data-bound control that is
composed of other server controls.

How can I have an ASP.NET templated composite control?

I am trying to add a template to a simplified composite control containing a Label and a TextBox. I want my mark up to look something like this:
<test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
<TestTemplate>
<i>
<%# Container.Title) %></i>
<br />
<%# Container.Text %>
</TestTemplate>
</test:FormItem>
I have a templateContainer class that has properties for the TextBox and Label.
public class TemplateContainer : WebControl, INamingContainer
{
public TextBox Text { get { return m_item.Text; } }
public Label Title { get { return m_item.Title; } }
private FormItem m_item;
public TemplateContainer(FormItem item)
{
m_item = item;
}
}
In the main FormItem class I have a CreateControlHierarchy() method that is being called from CreateChildControls():
protected virtual void CreateControlHierarchy()
{
m_itemTemplateContainer = new TemplateContainer(this);
TestTemplate.InstantiateIn(m_itemTemplateContainer);
Controls.Add(m_itemTemplateContainer);
}
What I WANT is for the Template to render the actual control. Instead, it's calling ToString() on the control and displaying System.Web.UI.WebControls.Label and System.Web.UI.WebControls.TextBox. Is there a way to make the template add the controls to it's collection instead of just calling ToString() on them?
Note: I've also tried adding the textbox and label to the controls collection of the container which does the same thing.
Ok. So I tried a few things and I came up with an OK solution.
First, I tried to use methods in the data binding expression and then keep track of where in the container's Control collection the textbox or label would go. However, the CompiledTemplateBuilder (which is what .Net internally builds for ITemplates specified in mark up) put all of the markup before and after both binding expressions into one DataBoundLiteral control and the Control collection was already built when the method was called.
What did work was to create a new WebControl which serves as a place holder for the controls within the composite control. It has one property Control and when set, it add the control to it's Controls Collection.
public class FormItemPlaceHolder : WebControl, INamingContainer
{
public WebControl Control
{
get
{
if(Controls.Count == 0)
return null;
return Controls[0] as WebControl;
}
set
{
if (Controls.Count != 0)
Controls.Clear();
Controls.Add(value);
}
}
}
Then in the mark up, I create a control of this type and bind it's Control property to the correct property in the container.
<test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
<TestTemplate>
<i>
<test:FormItemPlaceHolder ID="ph" runat="server"
Control='<%# Container.Title %>' />
</i>
<br />
<test:FormItemPlaceHolder ID="ph2" runat="server"
Control='<%# Container.Text %>' />
</TestTemplate>
</test:FormItem>
Does anyone have a better solution?
The container should not define the controls, just the data.
It is in the markup that you should define the actual controls of the data, and assign them the values in from the container.
E.g.
public class TemplateContainer : UserControl
{
public string Text { get { return m_text; } }
public string Title { get { return m_title; } }
private string m_text;
private string m_title;
private FormItem m_item;
public TemplateContainer(FormItem item)
{
m_item = item;
}
}
And in the markup:
<test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
<TestTemplate>
<i><asp:Label runat="server" Text='<%# Container.Title) %>' /></i>
<br />
<asp:TextBox runat="server" Text='<%# Container.Text %>' />
</TestTemplate>
</test:FormItem>
If you are trying to create a composite control that does not require controls to be added in the markup, then why are you using a Template? If it is just for styling then perhaps creating your own Style object may be more effective?

Resources