I am struggling with something that I guess should be standard practice really. I have a number of user controls that use some JQuery plugins. I do not really want to link to the extra CSS and JS files from my main masterpage as this would cause extra load to the user the first time they hit the site, (admittedly it would only be the once), so I was just putting them links into the top of the user control. Then I looked at my source HTML, not nice! Even worse for controls that repeat multiple times on a page.
So I was thinking is there a way of injecting them into the Head of the page when they are needed from the User Control. For that matter is there a way of doing it to the footer for JS stuff?
To dynamically register a script (and ensure that duplicates are merged) in ASP.NET you can call:
Page.ClientScript.RegisterClientScriptInclude(
"mykey", "~/scripts/jquery-1.3.2.js");
And read the full details on this method on MSDN.
To add CSS dynamically you can do something like this:
HtmlLink cssLink = new HtmlLink();
cssLink.Href = "path to CSS";
cssLink.Attributes["some attr1"] = "some value1";
cssLink.Attributes["some attr2"] = "some value2";
Page.Header.Controls.Add(cssLink);
This example of injecting CSS will not merge duplicate entries. To avoid duplication you'll have to keep track of duplicates yourself. One place you can store a list of scripts you've already registered is in HttpContext.Items. Stick a HashSet in there that keeps a list of all registered scripts so that you don't register the same CSS file twice (which is generally harmless, but something to avoid anyway).
I followed a similar approach, but I use CSS directly in the user control so I don't have to import a CSS file. The following is code entirely from a sample user control:
<style id="style1" type="text/css" visible="false" runat="server">
td { font-family: Tahoma; font-size: 8pt; }
</style>
In code-behind:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
HtmlGenericControl style = new HtmlGenericControl("style");
style.Attributes.Add("type", "text/css");
style.InnerHtml = style1.InnerHtml;
Page.Header.Controls.Add(style);
}
You'll notice that the CSS is rendered in the head tag and not inside the body tag.
You can use ClientScript.RegisterClientScriptInclude() for the JavaScript.
For the CSS, one trick is to include them in your Master page, but with Visible="false", so that they aren't rendered into the markup by default.
Then, in your user controls, set a flag in the Items collection, from an early event, such as OnLoad(). For example, this.Context.Items["mycss"] = true;
Finally, in your Master page, from a later event, such as OnPreRender(), check to see if those flags are set. If they are, then set the Visible property to true for the corresponding CSS.
This also allows you to use the control with Master pages that don't use the CSS, since the Items entries could simply be ignored. If you have many Master pages that need the same behavior, you could put this code in a base class or use nested Master pages.
I assume you're using Asp.NET.
Try putting a content placeholder in the of the MasterPage...
<head>
<asp:ContentPlaceHolder ID="AdditionalPageHeader" />
</head>
If you're working in an aspx file or an ascx you need only define a content control...
<asp:Content ContentPlaceHolderID="AdditionalPageHeader" />
If you're working on a code-behind only type of server control, you can can get a pointer to that content place holder:
this.Page.Master.FindControl("AdditionalPageHeader")
... and manipulate it's contents programatically.
To add stylesheets or javascript (inline or not) dynamical I wrote these three functions:
Public Function addScript(ByVal path2js As String) As System.Web.UI.Control
Dim si As New HtmlGenericControl
si.TagName = "script"
si.Attributes.Add("type", "text/javascript")
si.Attributes.Add("src", path2js)
Return si
End Function
Public Function addScript_inline(ByVal js As String) As System.Web.UI.Control
Dim si As New HtmlGenericControl
si.TagName = "script"
si.Attributes.Add("type", "text/javascript")
si.InnerHtml = js
Return si
End Function
Public Function addStyle(ByVal path2css As String) As System.Web.UI.Control
Dim css As New HtmlLink
css.Href = path2css
css.Attributes.Add("rel", "stylesheet")
css.Attributes.Add("type", "text/css")
css.Attributes.Add("media", "all")
Return css
End Function
I call them in page_load on my masterpage, like this:
Me.Page.Header.Controls.Add(modGlobal.addScript("script/json/json2.js"))
or
Me.Page.Header.Controls.Add(modGlobal.addStyle("style/flexigrid/flexigrid.css"))
Regards
Related
The ASP.NET function ClientScript.RegisterClientScriptBlock can be used to register a chunk of JavaScript code that will be added to the page when it's rendered. The idea here is that you could have multiple instances of various user controls trying to register the same script over and over, but this ensures that it will only be included once.
The problem is, you don't really have any control over where the code is added to the page. This will insert the code inside the BODY tag of your page, but I need to add something (not limited to JavaScript code) into the HEAD block.
I'm well aware of various methods of adding something to the HEAD block via a ContentPlaceHolder block or by "Head.Controls.Add but these options do not address the problem of the same thing being added multiple times.
Is there an existing way to do this, or do I have to write a class that does something similar to ClientScript.RegisterClientScriptBlock except targeting the HEAD block?
I threw together a user control. There's nothing in the markup at all, so you can just add a new Web Forms User Control to your project and put this in the code behind:
public partial class ScriptControl : System.Web.UI.UserControl
{
private Dictionary<string, string> _scripts = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public void RegisterScript(string key, string script)
{
if(!_scripts.ContainsKey(key)) _scripts.Add(key, script);
}
protected override void Render(HtmlTextWriter writer)
{
writer.WriteFullBeginTag("script");
foreach(var script in _scripts.Values) writer.Write(script);
writer.WriteEndTag("script");
}
}
Just add the directive to your page markup:
<%# Register TagPrefix="uc" TagName="ScriptControl"
Src="ScriptControl.ascx" %>
(where "ScriptControl.ascx" is whatever you've named the control)
and then you can add it wherever you need to, including in the header.
<head runat="server">
<uc:ScriptControl id="HeaderScriptControl" runat="server"/>
</head>
The usage is pretty much the same:
HeaderScriptControl.RegisterScript("myScript",
#"alert(""hello, world!"")");
I have a specific control gridview , i apply specific CSS file on it , i wanna to change this CSS under specific condition in my .cs file, is there away to do that?
for example :
<ItemStyle CssClass ="normal"/>
i want to change this in .cs under specific condition.
Do you mean GridView?
It doesn't have and ItemStyle Property. Columns within it do though.
So you can use:
gv.Columns[0].ItemStyle.CssClass = "RedItem";
DataGrid does have an ItemStyle property:
dg.ItemStyle.CssClass = "MoreThanNormalClass";
Its not clear from your question what you want to change...
Example,
CSS Classes:
<style>
.Normal{ background-color:Lime; }
.Warning{ background-color:Red; }
</style>
ASP.NET markup:
<asp:DataGrid runat="server" id="dg" onitemdatabound="dg_ItemDataBound" >
<ItemStyle CssClass="Normal" />
</asp:DataGrid>
C# code behind :
protected void Page_Load(object sender, EventArgs e)
{
int[] someInts = { 1, 15, 20 };
dg.DataSource = someInts;
dg.DataBind();
}
protected void dg_ItemDataBound(object sender, DataGridItemEventArgs e)
{
if (e.Item.DataItem != null)
{
int v = (int)e.Item.DataItem ;
if (v > 10 && v < 20)
e.Item.CssClass = "Warning";
}
}
Outputs:
Your styles doesn't have to be in a .css file: You can change the file to an .aspx or maybe create an HttpHandler to serve up your CSS.
You have to get something like this rendered to the HTML page.
<style type="text/css">
.itemstyle
{
/* whatever styles you need */
}
</style>
This can override whatever you have in your CSS file. One option is to put a Literal control on the ASPX page:
<asp:Literal ID="litStyle" runat="server" />
and use that to write out the necessary styles from code behind:
litStyle.Text = "<style type=\"text/css\">.itemstyle{" + myStyles + "}</style>";
You can inject a <style> tag with the necessary adjustments to it onto the page without modifying the CSS file.
Just create a literal control, acting as placeholder for the modified styles, on the ASPX page. From the code-behind, render something similar to the following to the literal:
Literal1.Text = "<style type=\"text/css\">.normal { background-color:red; }</style>";
...with "normal" being the original CSS class that you want to modify. The beauty of CSS is that it would first apply styles set in the included files, then any "overwritten" styles explicitly specified on the page in <style> tags.
Dynamic CSS is generally frowned upon.
You should define two ( or more) classes within your CSS and then set the ClassName property of the element in a PreRender event( or client side) script as needed.
Well, controls aren't responsible of managing CSS source files and its addition to the page, so, the easy answer is no, you can't do that.
By the way, there's some solution for doing that.
You can include a CSS file with style HTML element from your control by adding a server control (HtmlGenericControl, for example), with the apropiate attributes and values, so, if container control requires some specific CSS file, you can add it during container control's life cycle, just before rendering it, to HTML head element (marked with runat="server" attribute) of some ASP.NET page.
Maybe a good way of doing that should be creating a configuration section in your web.config implementing your own one which may support creating dependencies of controls/pages and CSS stylesheet files, so, using this approach, you would be able to implement some method in a derived from System.Web.UI.Page class that may add CSS files depending on controls:
<cssDependencies>
<control type="YourNamespace.YourControl" cssFile="~/Styles/Default/YourControlStyle.css" />
<cssDependencies>
And then, your CustomPageBase would have its own "AddControl" method which should register its type in some collection that may be iterated in the PreRender method, so, there you can add CSS files based on control's types.
I'm just giving you ideas! :)
EDIT & NOTE:
Anyway, this approach, and your goal, could have problems in terms of performance optimization.
Best optimized sites should combine all needed CSS into one, so browser should load one instead of many during page renderization.
I believe combining all CSS files into one can be achieved with "CSS files and control types approach", and I would suggest you to go this way, because if you don't do that, you can end with pages having dozens of style elements.
Have you heard about DotLess project? Check it out here: http://www.dotlesscss.org/ Maybe it can give you a better approach with less effort!
The best way would be to have different classes set up in your static css-file and change which of these classes your gridview uses from codebehind (.cs). This way you will still get the benefit of the css being cached and well separated from your view (.aspx).
css:
.normal { background-color:white; }
.alternate { background-color:#EEE; }
codebehind
var css = SomeLogic() ? "normal" : "alternate";
gridView.RowStyle.CssClass = css;
I have a tricky problem and I'm not sure where in the view rendering process to attempt this. I am building a simple blog/CMS in MVC and I would like to inject a some html (preferably a partial view) into the page if the user is logged in as an admin (and therefore has edit privileges).
I obviously could add render partials to master pages etc. But in my system master pages/views are the "templates" of the CMS and therefore should not contain CMS specific <% %> markup. I would like to hook in to some part of the rendering process and inject the html myself.
Does anyone have any idea how to do this in MVC? Where would be the best point, ViewPage, ViewEngine?
Thanks,
Ian
You could use Html.RenderPartial to insert an HTML fragment somewhere in the page. If you want to insert it in a place not available to the view but only on the master you could place a <asp:ContentPlaceHolder ID="Admin" runat="server" /> placeholder inside the master and in the view simply override it and insert the partial. If placing such a placeholder is not acceptable you could use AJAX like: $('#adminHolder').load('/home/admin');, but I would probably go with the previous approach as it will work in case the user has javascript disabled.
OK this took a bit of messing and the result is a little hacky. But it works and that's all that matters right....
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
if (!User.Identity.IsAuthenticated || !User.IsInRole("Admin"))
{
// If not admin continue as normal
base.Render(writer);
return;
}
// Taking a leaf out of the move viewstate to the bottom of page playbook
var stringWriter = new System.IO.StringWriter();
var htmlWriter = new HtmlTextWriter(stringWriter);
base.Render(htmlWriter);
var html = stringWriter.ToString();
var endOfBody = html.IndexOf("</body>") - 1;
if (endOfBody >= 0)
{
var adminConsole = Html.RenderPartialAsString("AdminPanel");
html = html.Insert(endOfBody, adminConsole);
}
writer.Write(html);
}
I implement my own ViewPage overriding the Render method. This checks if the user is logged in as an admin and if they are, it renders a partial at the bottom of the page. Very similar to old skool viewstate hacks in webforms.
Enjoy.
As title, basically I have a user control placed inside a page and then the page is placed inside the master page. I want to extract a block of javascript and put it back to the page head. When I try to wrap the code inside any block control (e.g. a div runat server) and access divID.InnerText then ASP.NET bust off with
Cannot get inner content of startup_script because the contents are not literal.
I dont want to extract JS inside cs file, thats awfully ugly approach (all sort of escapes and people wont even notice you have JS written unless they drill your CS file), what can I do?
You could store the javascript in a separate file, and then add it to the page using Page.RegisterClientScriptBlock()
Add the javascript you want to a .js file and add the .js file to your project.
Alter the properties of the .js file so that it is an Embedded Resource.
Then use code like this somewhere in your UserControl (maybe the Page_Load) to pull the code from the file and drop it into the page:
string javaScript = "";
// the javascript is in a separate file which is an 'embedded resource' of the dll
using (StreamReader reader =
new StreamReader((typeof(ThisClass).Assembly.GetManifestResourceStream(typeof(ThisClass), "NameOfJavaScriptFile.js"))))
{
javaScript = String.Format("<script language='javascript' type='text/javascript' >\r\n{0}\r\n</script>", reader.ReadToEnd());
}
Page.RegisterClientScriptBlock("MyScriptBlock", javaScript);
Note that RegisterClientScriptBlock() will put the script near the top of the page, but apparently not in the page header.
(edited bit about header after comment)
<%# Page Language="C#"%>
<script runat=server>
protected override void OnLoad(EventArgs e)
{
string scriptText = someID.InnerHtml;
//if you really want it in the header...
//Page.Header.Controls.Add( new LiteralControl(String.Format( "<scr" + "ipt language=\"javascript\">{0}</scri" + "pt>\\n", scriptText )));
//doesnt add to header and requires form runat=server
Page.ClientScript.RegisterStartupScript(this.GetType(), "SomeScript", scriptText, true);
base.OnLoad(e);
}
</script>
<head runat=server></head>
<form runat=server>
<div id="someID" runat=server>alert('hi');</div>
</form>
Okay, I've probably done this in a horrible, horrible way, but I wanted to add a script to the head element in a recent project from a user control. This script didn't require any data from my server, but I only wanted it on specific pages, so I put the script in a .js-file, and added it to the head like this:
HtmlGenericControl script = new HtmlGenericControl("script"); // Creates a new script-element
script.Attributes.Add("type","text/javascript");
script.Attributes.Add("src","src/to/js-file.js");
Page.Master.FindControl("head").Controls.Add(script); // Finds the element with ID "head" on the pages master page.
Not entirely sure if the code works as I think it does as the code I wrote for the project is on another machine, but you get the idea, right?
Edit:
After googling your error message for a bit, I think this might be a solution to your problem:
using System.IO;
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
yourDivID.RenderControl(h);
String str = sw.GetStringBuilder().ToString();
If you combine the two examples, you should be able to get the result you want. I haven't tested this, but it works in my head.
How do you add Javascript file programmatically to the user control?
I want the user control to be a complete package - ie I don't want to have to add javascript that's related to the user control on the page where it's used, when I can do it inside the control itself.
Since there is no Page object in the user control, how would you do it?
In the Page_Load method of control.ascx.cs file:
LiteralControl jsResource = new LiteralControl();
jsResource.Text = "<script type=\"text/javascript\" src=\"js/mini-template-control.js\"></script>";
Page.Header.Controls.Add(jsResource);
HtmlLink stylesLink = new HtmlLink();
stylesLink.Attributes["rel"] = "stylesheet";
stylesLink.Attributes["type"] = "text/css";
stylesLink.Href = "css/mini-template-control.css";
Page.Header.Controls.Add(stylesLink);
This will load css and Javascript into the head tag of the main page, just make sure that the head has runat="server".
You can register client script includes using the ClientScriptManager.
Page is accessible through the Control.Page property.
Page.ClientScript.RegisterClientScriptInclude (
typeof ( MyControl ), "includeme.js", "js/includeme.js" );
EDIT: Sorry, for a total "complete package", its possible using scripts as Embedded Resources,
and aquire dynamic URL's through the WebResource.axd handler.
If this is not considered totally complete, then i guess it could be put in App_LocalResources, but it never gonna be just one file,
unless the code and script is inline.
The same as gnomixa's response, only a bit cleaner:
HtmlGenericControl js = new HtmlGenericControl("script");
js.Attributes["type"] = "text/javascript";
js.Attributes["src"] = "jscript/formfunctions.js";
Page.Header.Controls.Add(js);
from http://www.aspdotnetfaq.com/Faq/How-to-Programmatically-add-JavaScript-File-to-Asp-Net-page.aspx
In my site I Place all needed scripts and styles to placeHolder
<asp:placeHolder runat="Server" ID="phHead">
<script src="/header/widget/script.js" type="text/javascript"></script>
<link href="/header/widget/style.css" rel="stylesheet" type="text/css" />
</asp:placeHolder>
and
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Page.Header.Controls.Add(phHead)
End Sub
Good question!
A UserControl should be a single package without any dependency on a JavaScript or a CSS file. You will need to make the JS and CSS files as embedded resources. Right click properties and set build action to embedded resources. Then you need to inject the JavaScript and CSS files as WebResources.
Here is one article that I wrote yesterday which talks about the same scenario:
http://highoncoding.com/Articles/502_Creating_RadioButton_Validation_Using_Custom_Validator.aspx
I generally do it when rendering the HTML for the control and depending on whether it's a library injection or an instance injection I use the Items collection to specify whether or not I have already produced the code for a control of this type during the current request using a unique identifier.
Something to the effect of:
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
//Check if the Object Script has already been rendered during this request.
if (!Context.Items.Contains("xxx"))
{
//Specify that the Object Script has been rendered during this request.
Context.Items.Add("xxx", string.Empty);
//Write the script to the page via the control
writer.Write([libraryinjection]);
}
//Write the script that instantiates a new instance for this control
writer.Write([instanceinjection]);
}
If you have the text of the actual javascript in your .CS file, you can call Page.ClientScript.RegisterClientScriptBlock.
The following assumes that "GetScript()" returns the actual javascript you want added to the rendered control.
Page.ClientScript.RegisterClientScriptBlock(GetType(), "controlScriptName", GetScript());
I found this to be a more elegant way to fix-link to your javascript.
In your .ascx, link to your script files the following way:
<script src='<%= ResolveClientUrl("~/Scripts/jquery-1.7.1.min.js") %>' type="text/javascript"></script>
<script src='<%= ResolveClientUrl("~/Scripts/jquery.maskedinput-1.3.min.js") %>' type="text/javascript"></script>
That way, no matter which subfolder/page you add your user control to, the link to your scripts will always be correctly resolved.