ASP.net How to output cache a webusercontrol on controls public properties - c#

I have a web user control, it serves some potentially intensive data calculations and I would like it to be output cached so that each page view doesn't recalculate the data. It resides on very frequently viewed pages so it's quite important I get it working right!
For context, it's used on our arcade:
http://www.scirra.com/arcade/action/93/8-bits-runner
Click on stats, the data for the graphs and stats are generated from this webusercontrol.
The start of the control is as follows:
public partial class Controls_Arcade_Data_ArcadeChartData : System.Web.UI.UserControl
{
public int GameID { get; set; }
public Arcade.ChartDataType.ChartType Type { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
Now the difficulty I'm having is the output cache needs to be dependant on both the GamID and the ChartType.
This control is re-used with many different combinations of GameID's and Types, I need it to create a cache for each of these but am struggling to find out how to do this.
For example, one arcade game might pass in GameID = 93 and Type = GraphData, another might be GameID = 41 and Type = TotalPlaysData and another might be GameID = 93 but Type = TotalPlaysData. These should all return different data and have different output caches.
The control is used on the games page sort of like this (the parameters are actually set in the codebehind)
<div>Total Plays:</div>
<div class="count"><Scirra:ArcadeChartData runat="server" GameID="93" Type="TotalPlays" /></div>
<br /><br />
<div>Total Guest Plays:</div>
<div class="count"><Scirra:ArcadeChartData runat="server" GameID="93" Type="TotalGuestPlays" /></div>
etc.
Any help appreciated! I've spent a while looking online and it's kept coming up as something I need to solve but can't figure this one out.
Edit
Edit: I've tried adding this line to my control:
<%# OutputCache Duration="20" VaryByControl="GameID;Type" %>
But it just throws the error Object reference not set to an instance of an object. on the line where GameID is being set for the first time on the ASPX page using the control.

When a Control is retrieved from the output cache, it's not instantiated as an instance that you can manipulate; you just get the output the Control generated, not the Control itself. For example, you can't set properties on a cached Control from code behind, as you said in your question. The vary-by properties should be set declaratively (using an ExpressionBuilder might also work, though I haven't tried it).
To see in code behind whether a control has been retrieved from the output cache, check for null:
if (this.YourControlID != null) // true if not from cache
{
// do stuff
}
Even with that caveat, Control output caching is a bit quirky.
Try this:
<%# OutputCache Duration="20" VaryByControl="GameID;Type" Shared="true" %>
The output of the Control is stored in the output cache by associating it with a certain key. With Shared="true", the cache key is the value of all specified properties, together with the Control's ID. Without Shared="true", the cache key also includes the Page type, so the output would vary by Page -- which doesn't sound like what you want.
If you use the Control on more than one page, be sure to use the same ID on each page if you can, since the ID is included as part of the key for the output cache. If you can't or don't use different IDs, you will get a new copy of the Control's output in the cache for each unique ID. If the Controls with different IDs always have different property values anyway, that may not be an issue.
As an alternative to the OutputCache directive, you can set an attribute on the class declaration:
[PartialCaching(20, null, "GameID;Type", null, true)]
public partial class Controls_Arcade_Data_ArcadeChartData : UserControl

You need to take the following steps:
1) Add the following output cache directive to the page:
<%# OutputCache Duration="21600" VaryByParam="None" VaryByCustom="FullURL" %>
2) Add the following to global.asax:
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if (arg.Equals("FullURL", StringComparison.InvariantCultureIgnoreCase)
{
// Retrieves the page
Page oPage = context.Handler as Page;
int gameId;
// If the GameID is not in the page, you can use the Controls
// collection of the page to find the specific usercontrol and
// extract the GameID from that.
// Otherwise, get the GameID from the page
// You could also cast above
gameId = (MyGamePage)oPage.GameID;
// Generate a unique cache string based on the GameID
return "GameID" + gameId.ToString();
}
else
{
return string.Empty;
}
}
You can get more information on the GetVaryByCustomString method from MSDN and also review some of the other caching options here.

create a cache object in the code
HttpRuntime.Cache.Insert("ArcadeChartData" + GameID + Type, <object to cache>, null, System.Web.Caching.Cache.NoAbsoluteExpiration,new TimeSpan(0, 0, secondsToCache),CacheItemPriority.Normal, null);
above cache item will be enough to your work, but if you really want to use output cache as well try following code in the code behind,
Response.AddCacheItemDependency("ArcadeChartData" + GameID + Type);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(true);
Setting values for the page output cache is the same as manipulating
the SetExpires and SetCacheability methods through the Cache property.

I know that my solution may look very simple and possibly weird but I tried it and it works.
You simply have to add this line in your UserControl.
<%# OutputCache Duration="10" VaryByParam="none" %>
Note : I have only tested the Framework 4.0. Also if ever you have to change the value of the property in the UserControl (MyInt, My String in this example) do it in the Page_Init event.
Here is all my code :
Page :
<%# Page Title="Home Page" Language="vb" MasterPageFile="~/Site.Master" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="MyWebApp._Default" %>
<%# Register Src="~/UserControl/MyUserControl.ascx" TagPrefix="uc" TagName="MyUserControl" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<uc:MyUserControl ID="uc1" MyInt="1" MyString="Test" runat="server" />
<hr />
<uc:MyUserControl ID="uc2" MyInt="3" MyString="Test" runat="server" />
<hr />
<uc:MyUserControl ID="uc3" MyInt="1" MyString="Testing" runat="server" />
</asp:Content>
User Control:
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="MyUserControl.ascx.vb" Inherits="MyWebApp.MyUserControl" %>
<%# OutputCache Duration="10" VaryByParam="none" %>
<div style="background-color:Red;">
Test<br />
<asp:Label ID="lblTime" ForeColor="White" runat="server" />
</div>
User Control Code:
Public Class MyUserControl
Inherits System.Web.UI.UserControl
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Debug.Write("Page_Load of {0}", Me.ID)
Dim oStrBldr As New StringBuilder()
For i As Integer = 1 To Me.MyInt
oStrBldr.AppendFormat("{0}: {1} - {2} at {3}<br />{4}", Me.ID, i, Me.MyString, Date.Now.ToLongTimeString(), System.Environment.NewLine)
Next
Me.lblTime.Text = oStrBldr.ToString()
End Sub
Public Property MyInt As Integer
Public Property MyString As String
End Class
Please keep me posted, I have other solutions if ever you wish but they are more complex. I may also post with C#

One easy trick is to put all the graphics in a new page receiving GameId and Type as querystring parameters, use the out-of-the-box output cache for querystring parameters and the put an iframe in your page. Also you can make use of the browser's cache and never get the server hit for a while.

Ok, well the reason why this so hard to make OutputCache work in this case is because it wasn’t design to be use with Property’s, however it works very well with QueryString parameters. So my next solution isn’t the most professional and probably not the best, but it is definitely the fastest and the one that requires less code changing.
Since it works best QueryString, I recommend you putting your UserControl in one blank page, and wend ever you want to use your UserControl make an iframe that links to your page with the UserControl with QueryString.
Where you want to use your UserControl:
<iframe src="/MyArcadeChartData.aspx?GameID=93&Type=TotalPlays"></iframe>
Full page markup, MyArcadeChartData.aspx
<%# Page ... %>
<%# OutputCache Duration="20" VaryByParam="GameID;Type" %>
<Scirra:ArcadeChartData ID="MyUserControlID" runat="server />
Full page code, MyArcadeChartData.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
//TODO: Put validation here
MyUserControlID.GameID = (int)Request.QueryString["GameID"];
MyUserControlID.Type = (YourEnum)Request.QueryString["Type"];
}
Please not that values in the QueryString can be seen by the user, please do not put sensitive data.
Also I’m aware that this isn’t the most professional solution, but it is the easiest to implement, from what I know.
Regards and happy holidays

If I understand right, the caching isn't working correctly because of the way you have the properties supplying the values to the control, which probably has to do, in part, with the calculations that are being done.
You could create an HttpHandlerFactory that takes the request, does your calculations if they're not in the cache (inserting into the cache afterwards), handles the expiration of values, and then passes the request on to the page. It wouldn't be control-specific at all. That way you could use these calculated values in any control or page, and wouldn't have to implement caching policies that worry about their own calculations.

If this isn't data intensive, have you considered storing it in the Session as apposed to caching it? Just a thought...
Arcade.ChartDataType.ChartType Type;
string GameKey = GameId + Type.toString();
storedData = callCalculation(GameId,Type);
Session[GameKey] = storedData;
I realize this isn't in the cache, I am just trying to be constructive.

Related

Theme + dynamic control creation + Invisible UserControl = view state error?

default2.aspx
<%# Page Language="C#" AutoEventWireup="true" Theme="Blue" %>
<%# Register TagPrefix="uc1" TagName="favicon" Src="~/FavIcon.ascx" %>
<!DOCTYPE html>
<script runat="server">
private void Page_PreRender(object sender, System.EventArgs e)
{
HtmlGenericControl scriptControl = new HtmlGenericControl("script");
Page.Header.Controls.AddAt(0, scriptControl);//If this line is commented out, no exception will occur.
}
private void Page_Init(object sender, System.EventArgs e)
{
ScriptManager oSM = new ScriptManager();
Page.Form.Controls.Add(oSM);//If this line is commented out, no exception will occur.
}
</script>
<html>
<head runat="server">
<title></title>
<uc1:favicon runat="server"></uc1:favicon>
</head>
<body>
<form id="form1" runat="server">
<asp:Button ID="Button1" runat="server" Text="Button" />
</form>
</body>
</html>
FavIcon.ascx
<%# Control Language="C#" ClassName="FavIcon" AutoEventWireup="true" %>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
this.Visible = false;//If this line is commented out, no exception will occur.
}
</script>
Also add a stylesheet to the Blue theme.
The page opens successfully, but once I click the button, it throws exception
Failed to load viewstate. The control tree into which viewstate is
being loaded must match the control tree that was used to save
viewstate during the previous request. For example, when adding
controls dynamically, the controls added during a post-back must match
the type and position of the controls added during the initial
request.
Can anyone explain why this error happens?
To debug these errors, I strongly recommend to enable traces (Trace="true" in the #page directive, or use a global setting in web.config) so you can see the server's control tree.
On a GET, the control tree is this (note the unique IDs that were generated automatically):
HtmlHead
+ ctl05 : HtmlGenericControl (from your PreRender code)
+ ctl01 : Title (the <title> tag)
+ ctl02 : FavIcon (your favicon.ascx)
+ ctl04 : HtmlLink (from the 'Blue' theme)
HtmlForm
+ ctl03 : ScriptManager
The root causes of the 'Failed To Load ViewState' error are:
you use the ViewState. This is an option that in many cases can be disabled.
you don't use fixed IDs (that's why the Visual Studio's IDE adds automatic but fixed IDs to controls all the time).
you add controls dynamically, late, in the tree, as it changes the head's control tree. Plus, you add it before others (AddAt).
The generated IDs follow ASP.NET's lifecycle order:
Title (static)
FavIcon (static)
ScriptManager (Init)
Theme (Init, after overrides)
Dynamic controls (After Init in this case)
The ASP.NET ViewState engine is a tree serializer/deserializer. Each node in the tree has it's IDs, plus a "full ID" composed of its parent's IDs and its own ID. Of course, as soon as you change IDs between serialization and deserialization, all bets are off, the engine will detect it and raise the 'Failed to Load ViewState' error.
So, if you put Visible=false, you'll use the ViewState. If you remove it, you won't. When you don't use the ViewState yourself, there are less chances to get the 'Failed to load viewstate' error, but ASP.NET can use some ViewState on your behalf when you use all its features (Theme, etc.). In the case of Visible, it just means the control is there (and uses ViewState), but not rendered (it's Render Size is 0). But you'll see the problem with any other property that use the ViewState, it's not specific to the Visible property (you could also try this.ViewState["test"] = "whatever" ).
If you put <uc1:favicon runat="server"></uc1:favicon> elsewhere in the page, it will also work because it won't be in the Head control tree anymore, and won't interfere with Theme's link or your dynamic control.
For Theme and ScriptManager, it's just that when you play with them, it changes, or not, the IDs, and the system may detect it or not.
There is an infinite number of ways to break the ViewState. What's really difficult is it may seem to work when it shouldn't (for example as long as you don't use FavIcon's ViewState, you don't notice there is an issue with your code).
It happens due to the nature of ViewState. Briefly, when your page is being posted back a control into which ViewState is restored is matched by the control index, so when the index changes it causes this problem.
In this case, setting the property this way ViewStateMode="Disabled" for your page or changing the order you add your dynamic elements Page.Header.Controls.AddAt(Page.Header.Controls.Count, scriptControl), which lets you not interfere with the order of restored elements, can help you cope with the trouble. At least, it works out for me.

How to pass a class model/object from codebehind to aspx page?

How do I pass a class model object to my aspx page from code behind. In ASP MVC I can do it easily using a view model. Unfortunately I'm stuck
with a asp webforms application that requires some enhancements and I'm not to familar with asp.net webform and repeater controls.
protected void Page_Load(object sender, EventArgs e)
{
var orders = new List<Orders>();
// Do some stuff and get orders
var userInfo = new UserInfo()
{
Name = "Joe Blogs,
Age = "25",
Address = "123 Green Lane",
Orders = orders
};
}
I know I can create server variables and pass them individualy as follows but really I need to pass an object as it may containt an IEnumerable value that I need to iterate over in my aspx page. So I'm attempting at something like below:
<% foreach (var order in UserInfo.Orders) { %> <!-- loop through the list -->
<div>
<%= order %> <!-- write out the name of the site -->
</div>
<% } %>
Any help appreciated.
You can have a property, or a method, which return the object, and call it on the page. Make sure to use the right markup. In data bound context use:
<%# GetUser() %>
outside of that use
<%= GetUser() %>
Few considerations:
Make sure that the final output of expressions inside these tags is a string.
GetUser or however you call it cannot be private
In data binding context (e.g. inside gridview, repeater or such) this method will be called when the control is data bound
Outside of data binding context this code will be executed really early - don't rely on fetching data from DB on Page_Load, that is too late
However I would like to mention that you may want to reconsider the whole approach here. Plain foreach iteration inside aspx is usually a bad practice. Besides it will be difficult to get this markup right. Why do that when you can have a repeater bound to your data?
<asp:Repeater ID="Repeater1" ...>
<ItemTemplate>
<%# Eval("OrderName") %>
</ItemTemplate>
</asp:Repeater>
Repeater1.DataSource = userInfo.orders;
Repeater1.DataBind();
That would look much cleaner than any iteration inside aspx.

Access Textbox on Page from User Control in ASP.net

I have a some pages that are slightly different, but all have the same "action buttons" that do the same tasks for each page. Instead of duplicating the code, I made a user control that includes buttons that perform actions - but there's one action I can't seem to do.
Each page has a textbox (that's not inside the user control, as it's in a different location of the page). When I click the "Save comment" button (that is within the User Control), I can't seem to access the text in the Textbox.
I have tried using something like this:
TextBox txtComments = (TextBox)this.Parent.FindControl("txtComments");
SaveChanges(txtComments.Text);
...but txtComments comes back as null.
So, I'm wondering if this is possible, or perhaps if there's a better way to do what I'm trying to do?
Edit: The Textbox is in a Placeholder on the original page...
Edit 2: Posting minified solution - still can't figure this one out.
Edit 3: Removed solution to conserve space - resolved the issue.
My solution ended up being surprisingly simple....
TextBox txt = this.Parent.Parent.FindControl("ContentPlaceHolder2").FindControl("TextBox1") as TextBox;
if (txt != null) Label1.Text = txt.Text;
Where I was going wrong before was that I was using the wrong ContentPlaceHolder ID. I was using the ID of the placeholder in the page, rather than the ID from the Master Page.
Use the Page property exposed by WebControl, the common base of server-side controls.
You could even then cast the instance to the specific page type and access the control directly (if scope allows), instead of using FindControl.
To recap the situation - you need to do a FindControl of a control on a page from a child control, however -
Your project has a MasterPage, in which case this.Page seems to not work, and we use this.Parent instead
Your "target" control is inside a PlaceHolder, which itself is inside a ContentPlaceHolder, so it's not as simple as just this.Parent.FindControl()
Your child ASCX control that is trying to find the "target" control, in this case a textbox, is actually in ANOTHER ContentPlaceHolder, so again, this.Parent.Parent or whatever will not work.
Since you mentioned after my initial this.Parent answer about the controls being in a different ContentPlaceHolder from each other, and in another child control, it complicates your query a bit.
Based on these criteria, and the fact that you at least know the contentPlaceHolder control which contains (somewhere inside of it) your target TextBox, here's some code I wrote that works for me in a new ASP.net Web Forms application:
It recursively checks controls collection of the ContentPlaceHolder you pass to it, and finds your control.
Just pass the ControlID and ContentPlaceHolderID, and it will recursively find it.
This code is a replacement for my original one below with the same project, located inside of ChildControl.ascx.cs file:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Linq;
using System.Collections.Generic;
namespace FindControlTest
{
public partial class ChildControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
var textBoxTest = FindControlInContentPlaceHolder("TextBoxTest", "FeaturedContent") as TextBox;
Response.Write(textBoxTest.Text);
Response.End();
}
private Control FindControlInContentPlaceHolder(string controlID, string contentPlaceHolderID)
{
if (null == this.Page ||
null == this.Page.Master)
{
return null;
}
var contentPlaceHolder = this.Page.Master.FindControl(contentPlaceHolderID);
var control = getChildControl(controlID, contentPlaceHolder);
return control;
}
private Control getChildControl(string controlID, Control currentControl)
{
if (currentControl.HasControls())
{
foreach(Control childControl in currentControl.Controls)
{
var foundControl = childControl.FindControl(controlID);
if (null != foundControl)
{
return foundControl;
}
else
{
return getChildControl(controlID, childControl);
}
}
}
return null;
}
}
}
Note:
I tried this in a few events and even on Init() I was able to get the TextBox value
If you are seeing a null it is likely due to an incorrect ID being passed or a situation I didn't encounter yet. If you edit your question with additional info (as there has been a lot of it) and show what variable is null, it can be resolved.
Note that I added some complexity to my MasterPage, such as a PlaceHolder inside a Panel, and then put the ContentPlaceHolder in there, and the code still works. I even compiled for .net 4.5, 4.0, 3.5, and 3.0 thinking maybe FindControl works differently with MasterPages, but still it works every time. You may need to post some additional MarkUp if you still get a null.
The Rest of the Test Project
The Page (based on default MasterPage)
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FindControlTest._Default" %>
<%# Register TagName="ChildControl" TagPrefix="uc1" Src="~/ChildControl.ascx" %>
<asp:Content runat="server" ContentPlaceHolderID="FeaturedContent">
<asp:PlaceHolder ID="PlaceHolderTest" runat="server">
<asp:TextBox ID="TextBoxTest" Text="Hello!" runat="server"/>
</asp:PlaceHolder>
</asp:Content>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<uc1:ChildControl id="ChildControlTest" runat="server" />
</asp:Content>
I added a control called ChildControl.ascx that only has this in it:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="ChildControl.ascx.cs" Inherits="FindControlTest.ChildControl" %>
Hello child!
The result is "Hello!" on the page.
If you want to access the textbox property in codebehind as intellisence property, simply make it a string property in the user control.
1. In the user control, create a public string property that returns textbox string value..
public string MyText
{
get
{
return txt1.Text;
}
}
2. Register the user control on the page
<%# Register TagPrefix="uc" TagName="MyControl" Src="~/mycontrol.ascx" />
and declare it..
<uc:MyControl ID="control1" runat="server" />
3. From the codebehind now you can read the property..
Response.Write(control1.MyText);
Hope it helps
Thanks...

ajaxfileupload multiple inputs on page

I'm using ajaxFileUpload as described here: http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/AjaxFileUpload/AjaxFileUpload.aspx
It is working fine except when I have multiple file upload controls on the same page. Specifically, I am trying to upload different files for different questions. When I upload the first on the page, it works fine, but the one lower down on the page will only upload it's file into the answer for the first question.
I'm not sure that makes sense... so it may help you to know that my page is populated with questions dynamically using ascx files. The document ascx file looks like this:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="Document.ascx.cs" Inherits="ScholarshipApplication.controls.questions.Document" %>
<ajaxToolkit:AjaxFileUpload OnUploadComplete="UploadComplete" ID="FileUploadControl" MaximumNumberOfFiles="1" runat="server" AllowedFileTypes="png,jpg,jpeg,pdf,tiff,tif,gif" />
<asp:LinkButton ID="downloadButton" runat="server" CausesValidation="false" OnClick="downloadButton_Click" />
And the code behind:
public void UploadComplete(object sender, AjaxFileUploadEventArgs e)
{
entry.data = e.FileName;
entry.setDocumentData(e.GetContents());
this.downloadButton.Text = e.FileName;
}
My initial thoughts are that somehow I need to help the control's generated javascript to know which question it should be triggering when.
I believe this is a bug in control or this was implemented by some non-obvious reason. Actually, this control doesn't support multiple instances on a page. Consider to use AsyncFileUpload control instead or customize a bit sources of the AjaxFileUpload control. If you prefer second option then you need to download sources from here: http://ajaxcontroltoolkit.codeplex.com/SourceControl/BrowseLatest and change AjaxFileUpload.cs file (here is a path: /Server/AjaxControlToolkit/AjaxFileUpload/AjaxFileUpload.cs). What you need to do is to change ContextKey constant to property for combining context key guid with unique id of control:
public class AjaxFileUpload : ScriptControlBase
{
private const string ContextKeySuffix = "{DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}";
private string ContextKey
{
get { return this.UniqueID + "_" + ContextKeySuffix; }
}
Actually, if you'll look on PreRender method of AjaxFileUpload class you'll easy realize reson for such behavior of this control (the first control handle uploads from all sibling controls on a page).
as per my understanding You need a hidden field variable to identify your question id IN UserControl:
<input type="hidden" id="hdnQuestionId" runat="server"/>
while populating/generating question you need to set this variable , and when you upload the doc , fetch this hidden value and use it.
I created a data attribute named "data-upload-type" on ALL AjaxFileUpload controls and set it to the name of the type. Then I set up the client call to grab that value and set a cookie with the same value. The cookie IS received on the server side functions and I branch based on the value I receive.
Here is an example:
function StartUpload(sender, args) {
var t = $(sender._element).attr('data-upload-type');
document.cookie = 'upload-type=' + $(sender._element).attr('data-upload-type') + ';';
}
<asp:AjaxFileUpload ID="afuUploader1" runat="server" OnClientUploadStart="StartUpload" OnUploadComplete="UploadComplete" OnClientUploadComplete="UploadComplete" data-upload-type="UploadType2"></asp:AjaxFileUpload>
Then in your server side upload call simply check Response.Cookies("upload-type").
Works like a charm!

Using a custom control more than once on the same page / form .net

I have been building a custom control for some time now and overcome a number of hurdles. One challenge I have yet to resolve is the ability to use a custom control more than once on the same page.
I have a custom control that functions well on its own, but when two of the same controls are placed on the page the second control is able to control the first one. My guess is that the first one (control) is the first object and the second one is the same object. How can I make sure in the code that if I use the same control more than once on a page it will behave as two separate controls. Are there any specific things I should look at to make sure it allows it to be on a page more than once.
Thanks in advance.
When you add multiple instances of a control, be sure to give them different IDs. Then when writing any code that will interact with them, reference them by that ID.
<%# Register Src="controls/myControl.ascx" TagName="myControl" TagPrefix="uc1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainPlaceHolder" runat="server">
<uc1:myControl ID="ctlFirst" runat="server">
<uc1:myControl ID="ctlSecond" runat="server">
</asp:Content>
Then in the code behind:
ctlFirst.SomeProperty = true;
ctlSecond.SomeProperty = false;
Place a couple of instances of your custom control onto an ASPX page then view the HTML source and have a look at all the element IDs generated on each of the control instances. ASP.NET will automatically mangle the IDs of your control's children, prefixing them with the ID of the parent control. If you're outputting raw HTML, this might not happen. If there are any duplicate IDs, then that may be the cause of your problem, particularly if you're using client-side logic to manipulate the controls on the page.
Also, make sure that you're not using any session or application variables in your controls.

Categories

Resources