I creating the "clone" of LoginView ASP.NET control in ASP.NET 3.5, for our company needs and I'm trying to understand how it achieved the ability to set controls with same ID in different templates.
For example this is ASPX markup with LoginView control:
<asp:LoginView ID="lv" runat="server">
<AnonymousTemplate>
<asp:Label ID="lbl" runat="server" />
</AnonymousTemplate>
<LoggedInTemplate>
<asp:Label ID="lbl" runat="server" />
</LoggedInTemplate>
</asp:LoginView>
This is valid markup and page works fine.
Now I created my custom control:
[ParseChildren(true)]
[PersistChildren(false)]
public class ContentControl : Panel
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public PlaceHolder AnonymousView { get; set; }
[PersistenceMode(PersistenceMode.InnerProperty)]
public PlaceHolder LoggedinView { get; set; }
public ContentControl()
{
this.Init += new EventHandler(ContentControl_Init);
}
void ContentControl_Init(object sender, EventArgs e)
{
if (AnonymousView == null)
{
AnonymousView = new PlaceHolder();
}
if (LoggedinView == null)
{
LoggedinView = new PlaceHolder();
}
this.Controls.Add(AnonymousView);
this.Controls.Add(LoggedinView);
AnonymousView.ID = "AnonymousView";
LoggedinView.ID = "LoggedinView";
AnonymousView.Visible = !MyContext.IsLogged;
LoggedinView.Visible = MyContext.IsLogged;
}
}
Now when I use my control with following markup, I receive the error that control with ID "lbl" exists more than once on page:
<TL:ContentControl ID="c" runat="server" CssClass="dd">
<AnonymousView>
AnonymousView
<asp:Label ID="lbl" runat="server" />
</AnonymousView>
<LoggedinView>
LoggedinView
<asp:Label ID="lbl" runat="server" />
</LoggedinView>
</TL:ContentControl>
How I can allow to have controls with same ID in both templates?
You need to develop templated control: Building Templated Custom ASP.NET Server Controls
And use control that implement INamingContainer as template container: Use TemplateInstance.Single to avoid FindControls
Related
I've search a lot for this issue but nothing came up.
My problem is the following: I have a main view in which I want to load a user control with parameters when I click on a button but the user control won't show. The constructor of the user control is called and the parameters are set, even the page load event from the user control is called. What am i doing wrong?
Main.aspx:
<%# Page Title="Main page" Language="C#" AutoEventWireup="true" CodeBehind="Main.aspx.cs" Inherits="MainApp.Main" MasterPageFile="~/Site.master" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent"></asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<%-- some other fields which i send to the user control on click --%>
<div class="col-lg-1 col-sm-12 col-md-1">
<asp:Button runat="server" CssClass="btn btn-primary" CausesValidation="false" ID="generateData" OnClick="generateData_Click" Text="View info" />
</div>
<div runat="server" id="contentDiv">
<p>No info available atm.</p>
</div>
</asp:Content>
Main.aspx.cs button click event:
protected void generateData_Click(object sender, EventArgs e)
{
UserControl1 uc = (UserControl1 )this.LoadControl(typeof(UserControl1 ), new object[] { param1, param2, param3});
contentDiv.Controls.Clear();
contentDiv.Controls.Add(uc);
}
UserControl1.aspx.cs:
public partial class UserControl1: System.Web.UI.UserControl
{
public string Param3 { get; set; }
public string Param1 { get; set; }
public string Param2 { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
Page.DataBind();
}
public UserControl1(string param1, string param2, string param3)
{
Param1 = param1;
Param2 = param2;
Param3 = param3;
}
}
UserControl1.ascx:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="UserControl1.ascx.cs" Inherits="MainApp.UserControls.UserControl1" %>
<div>
<p style="color: red">Under construction</p>
</div>
The user control isn't visible on the main page and I don't know why.
PS: I know that i can send parameters as seen below but I don't understand why I cannot use the method described above.
UserControl1 uc = (UserControl1)this.LoadControl("~/UserControls/UserControl1.ascx");
uc.Param1 = "val1";
...
Here is the full explanation of why second method with LoadControl by type will not work: Asp.net Usercontrol LoadControl Issue.
The reason there is a difference is because in the second instance you
are bypassing the binding mechanism of the the two partial classes.
When you include the ascx control in the page as a normal declaration,
the controls declared within it (the label in your example) are
automatically instantiated by the framework as the class is
instantiated. This is a binding mechnism that reads the "front-end"
ascx file and instantiates objects in the class.
When you use LoadControl(string) - you are doing exactly the same
thing.. it uses a virtual path to get the "front-end" file and ensure
controls within it are instantiated.
However, when you use the new (to 2.0) LoadControl(type, object)
version, there is no virtual path available and it rightly so makes no
assumptions (as it cannot ensure that a type has a front end file).
I have a nested class that contains order items for an order:
public class Order
{
public int ItemID { get; set; }
public int CustomerId { get; set; }
...
public List<OrderItem> Items { get; set; }
}
public class OrderItem
{
public int ItemId { get; set; }
...
}
and I have this databound to an asp repeater
var result = [method to get data]
rptOrders.DataSource = result;
rptOrders.DataBind();
and in the ascx page I have
<asp:Repeater ID="rptOrders" runat="server" HideControlForZeroRows="true" ZeroRowsText="You have no orders.">
<HeaderTemplate>
<table>
<tr class="tableHeader">
<td>Order #</td>
...
</tr>
</HeaderTemplate>
<ItemTemplate>
<ItemTemplate>
<tr>
<td><a href='#'><%# Eval("ItemID") %></a></td>
...
</tr>
</ItemTemplate>
but I want to use an accordion to show hide the order details, an example is here: http://jsfiddle.net/Xqk3m/, which means I have to add rows to my repeater table for the nested class items.
How do I reference the nested class to have another repeater iterate over the elements to add a for each of the nested items?
I have looked around the web and generally they are suggesting that I have a nested repeater call the nested class separately but really I want to make use of the fact that I have well defined classes. Hopefully I can do something like:
//parent repeater
<asp:Repeater ID="rptOrders" runat="server" HideControlForZeroRows="true" ZeroRowsText="You have no orders.">
....
//nested repeater
<asp:Repeater ID="rptOrderItems" runat="server" HideControlForZeroRows="true">
but how can I reference the order items for the order?
<asp:Repeater runat="server" DataSource="<%# Container.DataItem %>">
<ItemTemplate>
<tr>
<td><%# Eval("ItemId") %></td>
</tr>
</ItemTemplate>
</asp:Repeater>
You can add ItemDataBound event to rptOrders repeater:
protected void rptOrders_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
var order = (Order)e.Item.DataItem;
Repeater innerRep = e.Item.FindControl("rptOrderItems") as Repeater;
innerRep.DataSource = order.Items;
innerRep.DataBind();
}
In fact you can have any other data-bound control inside repeater item and bind it using list from DataItem.
I'm trying to create a custom Context Menu as a user control. The code behind for this user control looks like this:
public partial class UCContextMenu : UserControl
{
private List<ContextMenuItem> m_menuItems = new List<ContextMenuItem>();
protected void Page_Load(object sender, EventArgs e)
{
}
[PersistenceMode(PersistenceMode.InnerProperty)]
public List<ContextMenuItem> Items
{
get { return m_menuItems; }
set { m_menuItems = value; }
}
public event EventHandler Command;
}
Then, I defined the ContextMenuItem type in the same namespace as the user control (in the same file actually) which is essentially a menu item:
public class ContextMenuItem
{
public string Name { get; set; }
public string Text { get; set; }
}
The ascx page for the user control includes a repeater control which is defined like this:
<div id="contentHolder">
<ul id="ulContextMenu" style="display: none; z-index: 1000; border-width: 0.8px;
font-size: 13px;" class="ui-corner-all">
<asp:Repeater runat="server" DataSource="<%# Items %>">
<ItemTemplate>
<li><a href='#<%# DataBinder.Eval(Container.DataItem, "Name") %>'>
<%# DataBinder.Eval(Container.DataItem, "Text") %></a>
</li>
</ItemTemplate>
</asp:Repeater>
</ul>
</div>
And finally to use it in a page after registering it, I have:
<uc:ContextMenu runat="server" OnCommand="SomeMethod">
<Items>
<uc:ContextMenuItem Name="SomeName" Text="SomeText" />
</Items>
</uc:ContextMenu>
Now when I run this, I get a NullReference parse error on line
<uc:ContextMenuItem Name="SomeName" Text="SomeText" />
When not running, Visual Studio also says Element 'ContextMenuItem' is not a known element. It doesn't catch the exception at runtime either so maybe that way I could look at the stack trace to see what's going on. I was just wondering if anyone has encountered similar issues like this. Any help is appreciated.
Create ContextMenuItem as a normal user control. The fact that you are defining it in the same namespace as you have emphasized doesn't help you in any way.
So, just add ContextMenuItem.ascx to your project and then register and add the control on page or other control.
for example the user control i created is a dock panel, and i want to make it to allow add others controls from aspx page.
<uc1:dockPanel ID="dockPanel1" runat="server" >
--add control to here from aspx page--
</uc1:dockPanel>
You load controls dynamically via control.add method. First get an object of user control and then use control.add property for that.
See following link for the reference.
http://msdn.microsoft.com/en-us/library/aa287574(v=vs.71).aspx
in my research i found something like this.. so we can add an inner property inside the WebUserControl, for example.. but the problem that i facing now is the control is not render inside WebUserControl, its render in the page.
HTML
<uc1:dockPanel ID="dockPanel1" runat="server">
<ContentTemplate>
<dx:ASPxButton ID="ASPxButton1" runat="server" Text="ASPxButton">
</dx:ASPxButton>
</ContentTemplate>
</uc1:dockPanel>
ASCX
[ParseChildren(true)]
public partial class dockPanel :System.Web.UI.UserControl, INamingContainer
{
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateContainer(typeof(dockPanel))]
public ITemplate ContentTemplate { get; set; }
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (ContentTemplate != null)
ContentTemplate.InstantiateIn(this);
}
}
I have a user control which uses objects as inner properties (some code is below).
I am having trouble with setting the attribute of the Step class programmatically, when set programmatically it is being lost across postback which would indicate something to do with Viewstate (?).
When setting the property of the Step class declaratively it's working fine.
Does anybody have any ideas of what this code be/what's causing it to lose the state across postback?
ASPX Page
<uc1:StepControl ID="StepControl1" runat="server">
<Step1 Title="1. Select your Products" Enabled="true">
<Content>
<div class="clearfix">
<div class="floatRight">
<asp:Button ID="btnGoToStep2"
runat="server"
Text="Next"
CausesValidation="false"
OnClick="btnGoToStep2_OnClick" />
</div>
</div>
</Content>
</Step1>
<Step2 Title="2. Select your Features">
<Content>
<div class="clearfix">
<div class="floatLeft">
<asp:Button ID="btnBackToStep1"
runat="server"
Text="Back"
CausesValidation="false"
OnClick="btnBackToStep1_OnClick" />
</div>
<div class="floatRight">
<asp:Button ID="btnGoToStep3"
runat="server"
Text="Next"
CausesValidation="false"
OnClick="btnGoToStep3_OnClick" />
</div>
</div>
</Content>
</Step2>
</uc1:StepControl>
ASPX code behind
protected void btnGoToStep2_OnClick(object sender, EventArgs e)
{
StepControl1.Step1.StatusText = "4 Products Selected";
}
protected void btnBackToStep1_OnClick(object sender, EventArgs e)
{
// StatusText (of Step1) gets lost at this point.
}
User control code behind
public partial class StepControl : System.Web.UI.UserControl
{
[PersistenceMode(PersistenceMode.InnerProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[NotifyParentProperty(true)]
public Step Step1 { get; set; }
[PersistenceMode(PersistenceMode.InnerProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[NotifyParentProperty(true)]
public Step Step2 { get; set; }
protected void Page_Init(object sender, EventArgs e)
{
AddSteps();
}
private void AddSteps() { }
}
[Serializable()]
[ParseChildren(true)]
[PersistChildren(false)]
public class Step
{
[PersistenceMode(PersistenceMode.Attribute)]
public string Title { get; set; }
[PersistenceMode(PersistenceMode.Attribute)]
public string Status { get; set; }
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
[TemplateContainer(typeof(StepContentContainer))]
public ITemplate Content { get; set; }
public class StepContentContainer : Control, INamingContainer { }
}
I think the string you set never makes it to the ViewState. I am a bit short of terminology here (read: I do not know the terminology) but I think your attribute [PersistenceMode(PersistenceMode.Attribute)] only tells ASP.NET it should look for an attribute called "Status" in the markup (ASPX-file) and if it finds one, set the property Status to its value (actually I am wondering where exactly it is put in your example?). It does not tell anybody to put something into ViewState though.
If you would define your property Status along these lines
[PersistenceMode(PersistenceMode.Attribute)]
public string Status
{
get
{
object o = ViewState["Status"];
if(o != null) {
return (string)o;
}
return string.Empty;
}
set
{
ViewState["Status"] = value;
}
}
you should be better off.
For the rest of it I am not sure if you have to call TrackViewState() in UserControls or even override SaveViewState and LoadViewState but I do not think so. If this would be the case the following links might help:
Microsoft: Understanding ASP.NET View State
ASP.NET Forum: How to preserve ViewState in user control
It might have something to do with the order of the creation of your controls in the page. If after a postback, the controls are not created in the same order as for the first load of the page, then retreiving the viewstate willl not work for those controls.
How do you set the attribute of step programmatically?