When i need to set some value to a Javacript, or any other part of the code, i usually use this:
<script>
<%# SomeFunction() %>
</script>
And this also works for HTML in the document body, like...
<somehtmltag property="<%# SomeFunction2() %>">
And in the code behind i create the function that returns a string, with all the necessary code.
If i add some parameter to a user control like:
<ts:PeopleCard ID="us" runat="server" Visible="<%# IsVisivle() %>" />
It also works, but i try to create the entire user control it does not work.
<%# AddUserControl() %>
Function AddUserControl() as String
Return "<ts:PeopleCard ID=""us"" runat=""server"" Visible=""true"" />"
End Function
I understand that this does not work, because this code must be processed by the server to be converted in the actual code.
The final HTML, it shows:
<ts:PeopleCard ID="us" runat="server" Visible="true" />
when it shouldn't, it should show the processed HTML\css by the server.
So my question is, is it possible to create a control this way? Is it possible to force ASP.NET to "re-process" the page, after I changed its contents in code behind?
I understand there's several other ways to do it. Including, creating the user control in conde behind.
But i need to know, if is possible to do this way...
Usually you have a parent tag that is runat server and you can then add your own object to it.
Dim newTag as New PeopleCard
newTag.Visible = true
pnl.Controls.Add(newTag)
An other option I've done is the past is to add a RenderMethod to a control. Each control have a SetRenderMethodDelegate function and it allows you to write directly to the HtmlTextWriter. This won't create an object for the controls you create yourself.
If I did not have a rendering template I could add the following to my aspx page:
<asp:label ID="myLabel" runat="server" />
Then this in my code behind:
myLabel.Text = "Hello World";
But since my label is inside of a <SharePoint:RenderingTemplate> I am not able to access it the normal way.
Is there another way?
Edit: Ive found a number of articles like this one talking about creating a .dll for every rendering template. Is it really that complicated to output a string to a page? I should clarify that I am open to all ideas. I do not need code behind. I simply need to output a dynamic string to the template.
You can just create your own label control and work with it in any way you like.
There is an example here: Extending the Label Control, but it's basically like you would extend any other control.
What I would do then: Either set the .Text in that custom control directly, or what would be better is to use resource files. Then you could create your won property on the label like so:
<Custom:label ID="myLabel" specialresource="myLabel" runat="server" />
And in your class you could just handle the specialresource property and e.g. set the .Text to whatever you get from the resource.
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.
I have a label on a page which gets localized text through the meta:resourcekey attribute. The issue I have is that I want it to display different text depending on which view of a multiview they're on.
I tried adding the attribute though label.Attributes.Add("meta:resourcekey", "label"), but that doesn't seem to load any text. I tried it on PreRender, and same deal. The attribute appears when I look at the source, but no text is displayed.
Is this possible to do? The other option is to have 2 labels and change the visibility on page load, but that seems like the less elegant solution.
Thanks.
I think what you want for programmatic localisation in code behind is as simple as this:
ctrl.Text = (string)GetLocalResourceObject(“myCtrlKey.Text”);
ctrl.AnotherAttribute = (string)GetLocalResourceObject(“myCtrlKey.AnotherAttribute”);
Using LocalResource means that for a page called MyPage.aspx, you have created a resource file called MyPage.aspx.resx and/or MyPage.aspx.{culturename}.resx in the special directory App_LocalResource.
If you like Global Resources instead of local, use the special directory App_GlobalResource
to hold a resource file called MyResourceFileName.resx and call:
ctrl.Text= (string)GetGlobalResourceObject(“MyResourceFileName”, “myGlobalKey”);
copied from a blog about localization in the code behind
--
PS the reason that Attributes.Add("meta:resourcekey", "label") doesn't work is that "meta:resourcekey" isn't a real attribute and its use in the aspx is not really valid aspx markup - rather it's a preprocessing directive that causes the compiler to turn it into a longer list of attributes name/value pairs, based on what you've put in your resource file.
The approach of trying to assign a meta:resourcekey attribute will not work simply because they are treated specially by the page parser, and replaced before the page lifecycle code even really begins.
But meta:resourcekey is basically a declarative replacement for the code equivalent of accessing local resource files. In other words:
<asp:Label ID="MyLabel" meta:resource-key="MyResourceKey" />
is equivalent to:
<asp:Label ID="MyLabel" Text="<%$ Resources: myResXFile, MyResourceKey %>" />
is equivalent to the code:
MyLabel.Text = Resources.MyResXFile.MyResourceKey;
It looks like you're already dealing with your label in the code if you're trying to assign attributes to it. Why not set it's value in the code?
In a custom server control, I am simply writing out the number of child controls.
Is there a reason that the count would go to zero if <% %> tags are used within the body of the control tags?
Here's my extremely simplified control:
public class Script : Control
{
public override void RenderControl(HtmlTextWriter writer)
{
writer.Write(this.Controls.Count.ToString());
}
}
When passed only Literal data, the count written is 1 as expected:
<my:Script runat="server" ID="script3" >
function foo(){}
</my:Script>
When passed Literal data and some computed data, the count goes to zero:
<my:Script ID="Script1" runat="server" >
function foot(){}
<%=String.Empty %>
</my:Script>
There's nothing special about the String.Empty either. Anything I put here results in a count of zero.
Interestingly enough, other Control tags work fine however. The following counts 3:
<my:Script runat="server" ID="Script2">
hi
<asp:HyperLink runat="server" NavigateUrl="/" Text="hi" />
</my:Script>
Is there another way to get the child "content" of the custom control? I would think there is some way, as does it, but I can only inspect the metadata for System.Web.UI.WebControls.Content - not the implementation.
It's not possible to modify the Controls collection if your control has <%%> tags in the body (if you try to Add something, then you get an exception explaining just that). And for the same reason, the Controls collection is in fact empty. You can check if the collections is empty because of <%%> tags using the Controls.IsReadOnly property.
Turns out the answer was much more simple than the approach I was taking in the first place. Simply call the overridden RenderControl method with your own HtmlTextWriter and then use the captured markup however you want.
var htmlString = new StringBuilder();
var mywriter = new HtmlTextWriter(new StringWriter(htmlString));
base.RenderControl(mywriter);
Now the rendered markup is available in htmlString, regardless of <% %> tags used in the control's body.