Here's my control's code behind:
<PartialCaching(60, Nothing, "UsrCtl_WebUserControl.CacheString", Nothing, True)> _
Partial Class UsrCtl_WebUserControl
Inherits System.Web.UI.UserControl
Private _CacheString As String
Public Property CacheString() As String
Get
Return _CacheString
End Get
Set(ByVal value As String)
_CacheString = value
End Set
End Property
End Class
Here's the Control's Markup:
<%# Control Language="VB" AutoEventWireup="false" CodeFile="WebUserControl.ascx.vb" Inherits="UsrCtl_WebUserControl" %>
<span>Control Generated <%=DateTime.Now%></span>
It just outputs the current time.
Here's the user control embedded in a page:
<uc:wuc ID="wuc" runat="server" CacheString="A" />
And in another page:
<uc:wuc ID="wuc" runat="server" CacheString="B" />
According to the docs this control should maintain a different, 60 second cached version for each value of the CacheString property.
It doesn't work - it caches for 60 seconds, but only one cached copy is created regardless of what I put in the CacheString property.
Anyone any ideas what i'm doing wrong? - After 4 hours of this I have no hair or nails left - please save my monitor from the brick.
OK it's taken me a little while but I just replicated your problem. The problem crops up when the two controls have the same ID across multiple pages and in the constructor for the PartialCaching attribute, you set Shared to True. According to the documentation here the Shared property in the constructor is 'true to indicate that the user control output can be shared with multiple pages', which means, as you've seen, the first control to get loaded sets it and subsequent controls can only read what's already there. Under the covers it seems the control gets cached based on the ID of the control only without any regard to the page the control is on.
So, there are two potential solutions:
Change the ID of the control on the
page
In the PartialCaching constructor,
set Shared to false.
Related
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.
I am writing my very first asp.net application and am trying to use the PreviousPage property to access data between pages. Visual 2010 is giving me an error which I don't understand, so I need some help to understand what I am doing wrong.
I have an application where I will bounce between all the pages using Transfer. That way, from a user perspective there is only ever one url that he sees. This url will be the one that makes him log in to the application (a application controlled function for now) and connect to the database. I therefore have an sqlclient.sqlconnection object that I wish to hand off to the next page called _dbConnection this is a private variable in my page class declared ...
Partial Class Protocol
Inherits System.Web.UI.Page
Private _dbconnection As SqlClient.SqlConnection
Public ReadOnly Property dbConnection As SqlClient.SqlConnection
Get
Return _dbConnection
End Get
End Property
...
Later down the code, in reponse to a click event on a button
Server.Transfer("PSetup.aspx")
In PSetup.aspx I have the following
<%# Page Title="" Language="VB" MasterPageFile="~/MasterPage.master" AutoEventWireup="True" CodeFile="PSetup.aspx.vb" Inherits="PSetup" %>
<%# PreviousPageType VirtualPath="~/Protocol.aspx" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
Which should declare the Protocol class as the previous page
However in PSetup's Page_Load Sub I attempt this
_dbConnection = PreviousPage.dbConnection
where in this use _dbConnection is a private variable in the new page class.
Visual Studio is giving me an error 'dbConnection' is not a member of 'System.Web.UI.Page'
I have read through the documentation about this several times and I just don't understand what I am doing wrong. Could someone help me please.
I guess the first thing you need to understand is that ASP.NET is stateless. That means that no data is "saved" between pages. What the PreviousPage property does is it allows the request information persist through the use of a Transfer method, and the request information will be the GET or POST variables.
IMHO, the best way to do what you desire, is to have a shared public class that is not related to either page, and have a public function for getting the SqlConnection. Remember though, it is stateless, so you will have to create the connection each time.
Another alternative would be to save the dbConnection in the session (which I don't recommend, since it is possible to view the session information).
Otherwise, you can't have a variable persist with information between pages. Thats what is means to be stateless.
ALSO, for PreviousPage, keep in mind that it is creating an instance of System.Web.UI.Page, not an instance of Protocol. That means public properties won't exist from Protocol, only native features of Page will be there.
edit
From msdn
Note
Properties on the source page that are created primarily to expose values for cross-page posting are usually read-only properties. Although the source page can contain public read/write properties, setting a source page property from the target page property generally has no purpose, because the value will not be persisted.
try to make the dbConnection variable as Shared
Partial Class Protocol
Inherits System.Web.UI.Page
Private Shared _dbconnection As SqlClient.SqlConnection
Public Shared ReadOnly Property dbConnection As SqlClient.SqlConnection
Get
Return _dbConnection
End Get
End Property
But I'm not sure if it is a good practice passing data between pages that way
I am trying to pass a value between two pages. However on my target page, the value is not found.
My source page includes the following code.
Public ReadOnly Property SQLString() As String
Get
Return "SELECT * FROM City"
End Get
End Property
On my target aspx page I have included the following directive:
<%# PreviousPageType VirtualPath="~/tools/SearchResults.aspx"%>
In the target page code behind I have included the following in the page load:
Me.Master.Page.PreviousPage.SQLString
However, Visual studio complains that, SQLString is not a member of System.Web.Ui.page.
I must note I am useing master pages, and vaguely recall this causing an issue when accomplishing this in the past. Anyone have any ideas?
You need to cast the page to whatever type it actually is.
CType(Me.Master.Page.PreviousPage, ActualPageType).SqlString
In the code behind of your source page you will have a class declaration like the following:
Public Class ActualPageType
Inherits Page
Is it possible for a user control to determine its "context" or its parent .aspx page in some way?
Right now I have a user control that is declared on a typical .aspx page as follows:
<%# Register TagPrefix="uc1" TagName="ManageTitle" Src="../UserControls/ManageTitle.ascx" %>
The user control currently emits a textbox as follows:
<asp:textbox id="txtTitle" runat="server" MaxLength="60"
ToolTip="Describe the item with a short pithy title - most important keywords first"/>
The page_load for this .ascx file is currently like this:
Me.txtTitle.Text = SetPageTitle()
While some places in this web app need this (i.e. a textbox where end-user can type a "title"), I have other places where I want to show the "title" information in a "read-only" way. For example, rather than a textbox, I could use a label control or a textbox with Enabled="false" to prevent
data entry.
I suppose I could clone this small .ascx file and append a suffix to its name like _RO.ascx or something but I am wondering what the best approach would be.
In short, can a user control get some sort of "context" from the page that declares it or is there an altogether better way to accomplish this sort of thing? Thank you.
-- EDIT UPDATE WITH THE APPROACH SUGGESTED --------------------------
Code added to the UserControl:
Private mIsReadOnly As Boolean
Public Property IsReadOnly() As Boolean
Get
IsReadOnly = mIsReadOnly
End Get
Set(ByVal value As Boolean)
mIsReadOnly = value
End Set
End Property
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Page.IsPostBack Then
'Leave the textbox alone
Else
Me.txtTitle.Text = SetPageTitle() 'This is the original code
If IsReadOnly Then
Me.txtTitle.Enabled = False
Else
Me.txtTitle.Enabled = True
End If
End If
End Sub
Code added to the parent which invokes the UC:
<uc1:ManageTitle id="ManageTitle"
IsReadOnly="True" runat="server">
</uc1:ManageTitle>
If you want to follow common patterns, expose a public property on your .ascx control (in the codebehind) that allows the containing page to set its state programmatically. You could create a State property whose value is an enum of available States (readonly, editable, etc)
This is similar to a previous question. My answer there has a link showing how this can be done, as well as an alternate approach that's more "standard"
Can I get the currently opened ASP page file name in Visual Studio.NET in a custom control?
Yes, a control can access its context, e.g. using Me.Page (I hope this is how it's done in VB.NET) to access the page.
The problem with this approach is, that it makes your controls less reusable, because they will only work in contexts which they know. Probably it would be better to switch the dependency, i.e. the page/parent control should call a GetPageTitle() method of your control and do the appropriate things with the return value.
If I have a inline user control page, are public properties... properties on the control?
<%# Control language=C# %>
<script runat=server>
public string Title {get;set;}
</script>
ie. if someone loads the control by tag or programatically, they will see those public properties?
Yes.
However, if your user control is cached (with the #OutputCache directive), they won't, as ASP.NET will treat your control as a PartialCachingControl (even casting to UserControl won't work here). If you want to parameterize your user control, don't cache it. (But cache the page entirely for instance.)
Also note that if you're using LoadControl, you'll first need to cast the instance you receive to the proper user control type, otherwise the only way to use the properties is by using a) reflection, or b) late binding (VB.NET can do that for you IIRC).