ASP.NET - Nested Custom Templates - asp.net

I'm thinking about converting a few usercontrols to use templates instead. One of these is my own UC which contains some controls, one of which is a repeater. Is it possible to specifcy a template for the second level usercontrol from the template for the first (which would be on the page)?

Assuming I understand your question correctly, try something like this:
Page.aspx:
<%# Page Language="C#" %>
<%# Register src="UC.ascx" tagname="UC" tagprefix="uc1" %>
<uc1:UC ID="UC1" runat="server">
<RepeaterTemplate>
<%# Eval("Name") %> <%# Eval("Age") %><br />
</RepeaterTemplate>
</uc1:UC>
UC.ascx:
<%# Control Language="C#" ClassName="UC" %>
<script runat="server">
class Person {
public string Name { get; set; }
public int Age { get; set; }
}
protected void Page_Load(object sender, EventArgs e) {
repeater1.ItemTemplate = RepeaterTemplate;
repeater1.DataSource = new Person[] {
new Person { Name="Joe", Age=20},
new Person { Name="Jack", Age=30},
};
repeater1.DataBind();
}
public ITemplate RepeaterTemplate { get; set; }
</script>
<asp:Repeater runat="server" ID="repeater1">
</asp:Repeater>
This basically passes on the template specified on the outer page to the repeater in the user control.
It may not be exactly your scenario, but hopefully that'll give you ideas.

I'm not sure I understand your question, but I do alot of multi-level repeaters with dynamic templates. I use UserControls with no code as a convenient place to put the template html.
In code behind (such as an ItemDataBound event from a parent repeater), I select the appropriate template and set it:
repeater.ItemTemplate = MyBase.LoadTemplate(templateControlName)
repeater.DataSource = dataSource
repeater.DataBind()

Related

NullReferenceException in usercontrol from class library, but not from web project

I have following project structure where web application may contain user controls from the same web project or from a separate class library. The issue is in Default.aspx.cs, when I execute usercontrol2.SetValues(); I receive NullReferenceException since for some reason textbox2 is null. I've tried using EnsureChildControls(); but it does not help either. Should I register or invoke these usercontrols in some other way? Why does not it work out-of-box?
User control in web project
WebUserControl1.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="WebApplication1.WebUserControl1" %>
<asp:TextBox runat="server" ID="textbox1"></asp:TextBox>
WebUserControl1.ascx.cs
namespace WebApplication1
{
public partial class WebUserControl1 : System.Web.UI.UserControl
{
public void SetValues()
{
textbox1.Text = "Hello";
}
}
}
User control in class library
WebUserControl2.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl2.ascx.cs" Inherits="ClassLibrary1.WebUserControl2" %>
<asp:TextBox runat="server" ID="textbox2"></asp:TextBox>
WebUserControl2.ascx.cs
namespace ClassLibrary1
{
public partial class WebUserControl2 : System.Web.UI.UserControl
{
public void SetValues()
{
textbox2.Text = "world";
}
}
}
Main page
Default.aspx
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site1.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>
<%# Register tagPrefix="web" tagName="WebUserControl1" src="WebUserControl1.ascx" %>
<%# Register TagPrefix="web" Namespace="ClassLibrary1" Assembly="ClassLibrary1" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<web:WebUserControl1 runat="server" ID="usercontrol1"></web:WebUserControl1>
<web:WebUserControl2 runat="server" ID="usercontrol2"></web:WebUserControl2>
</asp:Content>
Default.aspx.cs
using System;
using System.Web.UI;
namespace WebApplication1
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
usercontrol1.SetValues(); // OK
usercontrol2.SetValues(); // NullReferenceException
}
}
}
Using ascx usercontrols from a different assembly is not possible.
I think you should look at this problem this way. Your classlibrary is compiled into a dll-file. This is the only thing that is available for your webapplication. The ‘ascx’ doesn’t get compiled into the dll, and is not available.
If you really want to keep some controls separated from your web-project, I think there are a few options:
Convert your usercontrol to a customcontrol. Custom controls are a bit more difficult to create, but it is certainly not impossible. This way you really get a re-usable control.
See also this question; it addresses your problem and there’s also a link to an algorithm which ‘converts’ an ascx usercontrol to a custom control. (I didn’t test that)
Asp.NET user control referenced from another project/dll has NULL properties
Make sure your ascx files are available to the web-project, by publishing to a path in your output. Then use LoadControl to load them serverside. This could work, but I also think some of the reusability from your classlibrary is lost.

w3wp eating up memory and not giving it back

I have a classic asp.net page in .net 4.6.1.
It loads 4 MB of data (they want it on one page) and no matter I simplify it, the IIS Worker Process w3wp.exe eats up a Gig of data, and never expires anything or gives any memory back.
Why?
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<asp:GridView ID="gvSelectionList" runat="server" AutoGenerateColumns="false" CssClass="LPSCriteriaSelection" EnableViewState="False">
<Columns>
<asp:TemplateField HeaderText="SerialNumber">
<ItemTemplate>
<asp:HyperLink ID="hlSerialNumber" Text='<%#GetSerialNumberText(Container.DataItem)%>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</asp:Content>
Here's the code
using System;
using System.Collections.Generic;
using System.Web.UI;
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(this.IsPostBack) return;
ExpandableSelections items = new ExpandableSelections();
if(items.Count == 0) return;
this.gvSelectionList.DataSource = items;
this.gvSelectionList.DataBind();
}
protected string GetSerialNumberText(object dataItem)
{
SerialNumberData item = (SerialNumberData)dataItem;
return item.SerialNumber;
}
}
public class SerialNumberData
{
public string SerialNumber { get; set; }
public SerialNumberData(string data) { SerialNumber = data; }
}
public class ExpandableSelections : List<SerialNumberData>
{
internal ExpandableSelections()
{ // Emulate database call
for (int i = 1; i < 72000; i++)
this.Add(new SerialNumberData("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
}
}
A call to Microsoft revealed calling .Dispose() on the grid itself will make it more readily available to Garbage Collection, but in this example the GC thread was often blocked (said they couldn't see by what -- antivirus?) and, because we only needed a read-only GridView,
I found it far better to simply replace the GridView with a classic <% FunctionCall(); %> and in that call write the html out directly with Reponse.Write().
This used far less RAM and would always return it on subsequent page loads.

I want to get property value from code behind

I have a case that I need to set the Text property for an asp label in the aspx page not from code behind. More exactly, I need to set a value to asp control in aspx page and this value is set by a property in the same page code behind.
so I need to use an expression to do that like:
<asp:Label Text="<%= MyProperty %>" ..../>
I use:
<%= MyProperty %> doesn't work.
<%# MyProperty %> doesn't also.
Default.aspx.cs
public partial class _Default : System.Web.UI.Page
{
public string CustomTitle = "This Is Title";
protected void Page_Load(object sender, EventArgs e)
{
Page.DataBind();
}
}
Default.aspx
<asp:Label Text='<%#CustomTitle %>' runat="server" />
You have to treat regular HTML and WebControls differently:
regular HTML:
Using <%= ... %> is sufficient:
<span><%= MyProperty %></span>
WebControls (stuff starting with <asp:...>):
<asp:Label Text='<%# MyProperty %>' />
In this case, you also have to call Me.DataBind() (VB) or this.DataBind(); (C#) in your codebehind, since <%# ... %> are data binding expressions.
Page.DataBind();
Do you call this in your code? It binds all variables set in code to your page.

problems with binding <%# in Custom Control

so I've finally started building custom controls instead of using functions which return chunks of HTML ;) But I'm running into a problem. I want to pass parameters to the control, say, "X":
<some:MessageControl runat="server" X=<%# "asd" %> />
My code behind looks like this:
public partial class MessageControl : System.Web.UI.UserControl
{
String x = "";
public String X
{
get { return x; }
set { x = value;}
}
}
When I output the value of x in the control,
x: <%= X %>
it is empty.
If I pass on "asd" directly as in
<some:MessageControl runat="server" X="asd" />
X gets the correct value.
What's happening here? How can I get this to work? Any suggestions are appreciated,
Nicolas
Edit: Some more context. Basically I want to be able to insert the control on a number of pages without settings its properties in the code behind, but still be able to set its visibility by calling a (varying) method from the containing page.
<%# Page Language="c#" Src="MyPage.aspx.cs" AutoEventWireup="true" Inherits="MyPage" %>
<%# Register Src="MessageControl.ascx" TagName="MessageControl" TagPrefix="some" %>
<html>
<body>
<some:MessageControl runat="server" Visible=<%# SomeBoolMethodFromContaining Page%> />
</body>
</html>
For <%= SomeMethods or Property %> expression you need to call DataBind() method in parent page or control that contains this expression on OnPageLoad event or another.
For example here code behind:
protected void Page_Load(object sender, EventArgs e)
{
DataBind();
}
protected string Hello
{
get { return "hello";}
}
Here html part of the page:
<asp:Literal runat="server" Id="Literal1" Text="<%= Hello %>"/>
For Visible property use code above and <%# Method or Property%> expression. For text use <%= %> expression. It renders output as a plain text.
Hope it will help you with your question.
Best regards, Dima.
Use this:
X='<%# "asd" %>'
Note the single quotes.

ASP.NET - Control Events Not Firing Inside Repeater

This is a absurdly common issue and having exhausted all of the obvious solutions, I'm hoping SO can offer me some input... I have a UserControl inside a page which contains a repeater housing several controls that cause postback. Trouble is, all of the controls inside of the repeater never hit their event handlers when they postback, but controls outside of the repeater (still in the UC) are correctly handled. I already made sure my controls weren't being regenerated due to a missing if(!IsPostBack) and I verified that Request.Form["__EVENTTARGET"] contained the correct control ID in the Page_Load event. I attempted to reproduce the symptoms in a separate project and it worked as it should.
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="NoteListControl.ascx.cs"
Inherits="SantekGBS.Web.UserControls.NoteListControl" %>
<asp:UpdatePanel ID="upNotes" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<div class="NoteList" id="divNoteList" runat="server">
<asp:Repeater ID="repNotes" runat="server">
<HeaderTemplate>
<table width="98%" cellpadding="3" cellspacing="0">
</HeaderTemplate>
<ItemTemplate>
<tr class="repeaterItemRow">
<asp:ImageButton ID="ImageButton1" runat="server" ImageUrl="~/Content/images/DeleteIcon.gif"
OnClick="ibRemove_Click" CommandArgument='<%# Container.ItemIndex %>' CommandName='<%# Eval("ID") %>'
CausesValidation="false" AlternateText="Delete" />
<%# Eval("Text") %></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
<asp:PlaceHolder ID="phNoNotes" runat="server" Visible="false">
<div class="statusMesssage">
No notes to display.
</div>
</asp:PlaceHolder>
</div>
</ContentTemplate>
</asp:UpdatePanel>
public partial class NoteListControl : UserControl
{
[Ninject.Inject]
public IUserManager UserManager { get; set; }
protected List<Note> Notes
{
get
{
if (ViewState["NoteList"] != null)
return (List<Note>)ViewState["NoteList"];
return null;
}
set { ViewState["NoteList"] = value; }
}
public event EventHandler<NoteEventArgs> NoteAdded;
public event EventHandler<NoteEventArgs> NoteDeleted;
public event EventHandler<NoteEventArgs> NoteChanged;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
UtilityManager.FillPriorityListControl(ddlPriority, false);
}
}
protected void ibRemove_Click(object sender, ImageClickEventArgs e)
{
System.Diagnostics.Debug.WriteLine("ibRemove POSTBACK"); // This is NEVER hit
}
public void Fill(List<Note> notes)
{
Notes = notes;
RefreshRepeater();
}
private void RefreshRepeater()
{
if (Notes != null && Notes.Any())
{
var sorted = Notes.OrderByDescending(n => n.Timestamp);
Notes = new List<Note>();
Notes.AddRange(sorted);
repNotes.Visible = true;
phNoNotes.Visible = false;
repNotes.DataSource = Notes;
repNotes.DataBind();
}
else
{
repNotes.Visible = false;
phNoNotes.Visible = true;
}
}
}
public class NoteEventArgs : EventArgs
{
public Note Note { get; set; }
public NoteEventArgs()
{ }
public NoteEventArgs(Note note)
{
this.Note = note;
}
}
The code is intentionally missing functionality so just disregard that fact.
Your edited code has residual CommandArgument and CommandName properties; are you actually handling the Repeater.ItemCommand event?
If so, and if your page calls the control's Fill method on postbacks, that would explain it.
This classic ASP.NET hair-tearing problem is explained in these posts: A Stumper of an ASP.NET Question and A Stumper of an ASP.NET Question: SOLVED!
The explanation is a little mind-bending, but the crux of it is that Repeater.DataBind interferes with ASP.NET's ability to determine which repeater button caused a postback.
I found a missing td-tag in the Itemtemplate, sometimes when DOM is incorrect, the updatapanel do strange things.
Just about EVERY time I run into this problem it's because DataBind() is being called when it shouldn't be. This will kill most events from controls inside a repeater. I see you have an !IsPostBack check in your Page_Load... so that's a start. But try putting a breakpoint on repNotes.DataBind() and see if it's getting called when you don't expect it.
Does it work OK outside of an UpdatePanel?
I ran into the same problem. It happened with me if I've ran the DataBind twice. In other words when I populate the repeater control twice (for any reason) the events wont fire.
I hope that helps.

Resources