I have a tree view created using HTML and Jquery only.
I would like to know can i create a custom control using just HTML and jquery to make it re-usable. If yes then can someone please throw some light on how i can achieve this
Thanks in advance
In order to make it reusable as an ASP.NET control, it also has to be naming container safe, if you want to be sure it can be safely dropped anywhere, or used more than once on a page. This means you need to be sure that you are passing the script a reference to a unique ID of the root of the control's markup (e.g. the ClientID), and any other references are relative to that.
But generally, you can make a custom control self-contained in several ways. If it has no controls, then you could include all the resources (HTML and script) as embedded resources. The control can at the simplest level stream its own embedded resources to the client in the Render method.
To deal with scripts, you can similarly use ScriptManager to register it's own embedded javascript as a client script block. This is not ideal, though, because your script will always be included in a block, and therefore probably not cached very well by the clients.
A better way is to serve embedded resources using WebResource.axd. Here is an article that explains how to do this.
Personally, I find WebResource and embedded resources to be a huge pain in the neck. You need to keep AssemblyInfo.cs in sync with the resources, and assembly name & namespace conventions always seem to be impossibly difficult to get correct. I have found it much easier to simply expose a static method of the custom control, e.g.:
public static Stream GetEmbeddedResource(someResourceIdentifier, out string mimeType) {
/// return a stream of my own embedded resource ... can be used for anything
/// css, js, html
}
Then just create your own web handler to use as a resource loader and stream the contents. This is so much easier, and you also don't end up with WebResource.axd includes with 200 character long query strings.
Related
Is there a way to reuse a piece of ASP.NET code (that tag-based code, not the code-behind) in many different pages? For example I have a <div> with some contents in it that appears in 5 different pages of my site. I'm looking for a technique through which I could save this <div> in a separate web-content file so to speak, with maybe a different extension like MyDiv.ASPC and could then do a server-side tag anywhere in a webpage like:
<asp:Import href="~/MyDiv.aspc" />
and ASP.NET would inject the entire content of this file at that point where this tag appears.
I thought of creating a user control for this, but I'm not sure if a user control always injects precisely what is written in its body, or could there sometimes be unwanted tags generated by user control itself.
Or are there existing better ways of doing this?
Edit
About Master Pages, they are far away from what I'm looking for. They are actually good for a common basic layout of your website. My idea is quite opposite of that. My pages do not have a common layout; it is just that they have one common <div>. It is more closely fulfilled by a UserControl.
For UCs, my fear is that they generate more than what is written in their body, whereas what I'm after is a simple text injection. To put it technically, what I'm looking for is basically a preprocessor step (kind of #include thing in C++) rather than a compiler step, if you see what I mean.
You need to use ASP.NET User Controls, as these are specifically created to be the solution to the problem you are describing. For more information, see MS Documentation.
From their documentation...
In addition to using Web server controls in your ASP.NET Web pages,
you can create your own custom, reusable controls using the same
techniques you use for creating ASP.NET Web pages. These controls are
called user controls.
A user control is a kind of composite control that works much like an
ASP.NET Web page—you can add existing Web server controls and markup
to a user control, and define properties and methods for the control.
You can then embed them in ASP.NET Web pages, where they act as a
unit.
An empty userControl would do just that - nothing. A user Control just adds it's contents to the page, or usercontrol hosting it. It adds nothing extra.
UserControls give you a nice easy page fragment type approach to reusing content. They work great within a project & most people use them for just that.
If you wanted to make something more reusable across projects, you could write server control. It's more involved, but much more reusable. Google should be able to find you many tutorials on how to do this.
Ran a short test. User Controls do not enter extra tags as long as you don't place any Runat="Server" tags in it, so this would indeed be a solution I guess.
You can also read output from a cache object where you would read your files.
So
<%= Static.ContentXyz %>
would mean:
public static class Static
{
public static string ContentXyz
{
get
{
string s;
if (!this.cacheDictionary.TryGetValue("ContentXyz", out s))
{
s = File.ReadAllText(Server.MapPath("ContentXyz.html"));
this.cacheDictionary("ContentXyz", s);
}
return s;
}
}
}
As a web developer I feel too much of my time is spent on CSS. I am trying to come up with a solution where I can write re-usable CSS i.e. classes and reference these classes in the HTML without additional code in ASPX or ASCX files etc. or code-behind files. I want an intermediary which links up HTML elements with CSS classes.
What I want to achieve:
Modify HTML immediately before transmission
Select elements in the HTML
Based on rules defined elsewhere (e.g. in a text file relating to
the page currently being processed):
Add a CSS class reference to multiple HTML elements
Add multiple CSS class references to a single HTML element
How I envisage this working:
Extend ASP.NET functions which generate final HTML
Grab all the HTML as a string
Pass the string into a contructor for an object with querying (e.g. XPATH) methods
Go through list of global rules e.g. for child ul of first div then class = "navigation"
Go through list of page specific rules e.g. for child ul of first div then class &= " home"
Get processed HTML from object e.g. obj.ToString
ASP.NET to resume page generation using processed HTML
So what I need to know is:
Where / how can I extend ASP.NET page generation functions (to get all HTML of page)
What classes have element / node querying methods and access to attributes
Thanks for your help in advance.
P.S. I am developing ASP.NET web forms websites with VB.net code-behinds running on ISS 7
Check out my CsQuery project: https://github.com/jamietre/csquery or on nuget as "CsQuery".
This is a C# (.NET 4) port of jQuery. In basic performance tests (included in the project test suite) selectors are about 100 times faster than HTML Agility Pack + Fizzler (a css selector add-on for HAP); it's plenty fast for manipulating the output stream in real time on a typical web site. If you are amazon.com or something, of course, YMMV.
My initial purpose in developing this was to manipulate HTML from a content management system. Once I had it up and running, I found that using CSS selectors and the jQuery API is a whole lot more fun than using web controls and started using it as a primary HTML manipulation tool for server-rendered pages, and built it out to cover pretty much all of CSS, jQuery and the browser DOM. I haven't touched a web control since.
To intercept HTML in webforms with CsQuery you do this in the page codebehind:
using CsQuery;
using CsQuery.Web;
protected override void Render(HtmlTextWriter writer)
{
var csqContext = WebForms.CreateFromRender(Page, base.Render, writer);
// CQ object is like a jQuery object. The "Dom" property of the context
// returned above represents the output of this page.
CQ doc = csqContext.Dom;
doc["li > a"].AddClass("foo");
// write it
csqContext.Render();
}
To do the same thing in ASP.NET MVC please see this blog post describing that.
There is basic documentation for CsQuery on GitHub. Apart from getting HTML in and out, it works pretty much like jQuery. The WebForms object above is just to help you handle interacting with the HtmlTextWriter object and the Render method. The general-purpose usage is very simple:
var doc = CQ.Create(htmlString);
// or (useful for scraping and testing)
var doc = CQ.CreateFromUrl(url);
// do stuff with doc, a CQ object that acts like a jQuery object
doc["table tr:first"].Append("<td>A new cell</td>");
Additonally, pretty much the entire browser DOM is available using the same methods you use
in a browser. The indexer [0] returns the first element in the selection set like jquery; if you are used to write javascript to manipulate HTML it should be very familiar.
// "Select" method is the same as the property indexer [] we used above.
// I go back and forth between them to emphasise their interchangeability.
var element = dom.Select("div > input[type=checkbox]:first-child")[0];
a.Checked=true;
Of course in C# you have a wealth of other general-purpose tools like LINQ at your disposal. Alternatively:
var element = dom["div > input[type=checkbox]:first-child"].Single();
a.Checked=true;
When you're done manipulating the document, you'll probably want to get the HTML out:
string html = doc.Render();
That's all there is to it. There are a vast number of methods on the CQ object, covering all the jQuery DOM manipulation techniques. There are also utility methods for handling JSON, and it has extensive support for dynamic and anonymous types to make passing data structures (e.g. a set of CSS classes) as easy as possible -- much like jQuery.
Some More Advanced Stuff
I don't recommend doing this unless you are familiar with lower-level tinkering with asp.net's http workflow. There's nothing at all undoable but there will be a learning curve if you've never heard of an HttpHandler.
If you want to skip the WebForms engine altogether, you can create an IHttpHandler that automatically parses HTML files. This would definitely perform better than overlaying on a the ASPX engine -- who knows, maybe even faster than doing a similar amount of server-side processing with web controls. You can then then register your handler using web.config for specific extensions (like htm and html).
Yet another way to automatically intercept is with routing. You can use the MVC routing library in a webforms app with no trouble, here's one description of how to do this. Then you can create a route that matches whatever pattern you want (again, perhaps *.html) and pass handling off to a custom IHttpHandler or class. In this case, you're doing everything: you will need to look at the path, load the file from the file system, parse it with CsQuery, and stream the response.
Using either mechanism, you'll need a way to tell your project what code to run for each page, of course. That is, just because you've created a nifty HTML parser, how do you then tell it to run the correct "code behind" for that page?
MVC does this by just locating a controller with the name of "PageNameController.cs" and calling a method that matches the name of the parameter. You could do whatever you want; e.g. you could add an element:
<script type="controller" src="MyPageController"></script>
Your generic handler code could look for such an element, and then use reflection to locate the correct named class & method to call. This is pretty involved, and beyond the scope of this answer; but if you're looking to build a whole new framework or something this is how you would go about it.
Intercepting the content of the page prior to it being sent is rather simple. I did this a while back on a project that compressed content on the fly: http://optimizerprime.codeplex.com/ (It's ugly, but it did its job and you might be able to salvage some of the code). Anyway, what you want to do is the following:
1) Create a Stream object that saves the content of the page until Flush is called. For instance I used this in my compression project: http://optimizerprime.codeplex.com/SourceControl/changeset/view/83171#1795869 Like I said before, it's not pretty. But my point being you'll need to create your own Stream class that will do what you want (in this case give you the string output of the page, parse/modify the string, and then output it to the user).
2) Assign the page's filter object to it. (Page.Response.Filter) Note that you need to do it rather early on so you can catch all of the content. I did this with a HTTP Module that ran on the PreRequestHandlerExecute event. But if you did something like this:
protected override void OnPreInit(EventArgs e)
{
this.Response.Filter = new MyStream();
base.OnPreInit(e);
}
That would also most likely work.
3) You should be able to use something like Html Agility Pack to parse the HTML and modify it from there.
That to me seems like the easiest approach.
I think I summed up the question in the title. Here is some further elaboration...
I have a web user control that is used in multiple places, sometimes more than once on a given page.
The web user control has a specific set of JavaScript functions (mostly jQuery code) that are containted within *.js files and automatically inserted into page headers.
However, when I want to use the control more than once on a page, the *.js files are included 'n' number of times and, rightly so, the browser gets confused as to which control it's meant to be executing which function on.
What do I need to do in order to resolve this problem? I've been staring at this all day and I'm at a loss.
All comments greatly appreciated.
Jason
If the issue is simply that the same file is being embedded multiple times and causing conflict, look into using RegisterClientScriptInclude. If you use the same identifier for all of your calls to RegisterClientScriptInclude only one instance of that file will be embedded:
http://msdn.microsoft.com/en-us/library/2552td66.aspx
However, if the issue is that your methods are being called but they don't know what controls on the page to operate on then you need to figure out how to provide your JS some context. Define a JavaScript object that represents your control on the client-side, and on the server-side emit the call that will instantiate it with the client IDs of the controls you'll be operating on.
We are using CustomValidator to validate User Control. The control works fine until you drop two instances of the Control on the same page, since they reference the exact same JavaScript functions, only one control works. Work around, we appended JavaScript function name with control id.
Validate_SAPDepartment<% =ControlId %>(oSrc, args) {...}
In codebehind, we assinged ClientValidationFunction
CustomValidator1.ClientValidationFunction = "Validate_SAPDepartment" + this.ControlId
This may not be the right approach but it works.
I've had this situation before. You register a separate JavaScript file with the page using the ScriptManager. You can stream this as a resource file embedded into the dll if you wish.
Then you only call into the functions from your control.
Otherwise a completely separate jquery file may also work.
I was wondering if its possible to have an ASP.NET AJAX custom usercontrol 'register' its use of CSS files like it does with JS files. ?
Eg: Implementing the IScriptControl interface allows for the GetScriptReferences() to be called by the Script Manager, is there a similar thing for CSS files?
No.
You could write your own methods to use embedded resources and generate html to write them to the page.
As a best-practice approach, though, I wouldn't recommend this. You should have a clear separation of style and markup. If you want to write a custom control that is dependent on a stylesheet, then I suggest that you provide a default css file along with your control.
EDIT (to answer questions in the comments)
You can run this code to add a reference to the CSS file to your page (at runtime). Note that because you're using the RegisterClientScriptBlock method, you can manage insert duplication. Here is an excerpt from the link for the method:
A client script is uniquely identified by its key and its type. Scripts with the same key and type are considered duplicates. Only one script with a given type and key pair can be registered with the page. Attempting to register a script that is already registered does not create a duplicate of the script.
StringBuilder sb = new StringBuilder();
sb.Append(#"<link rel=""stylesheet"" type=""text/css"" href=""");
sb.Append(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), resourceName));
sb.Append(#""" />");
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "MyCSS", sb.ToString());
I have a bunch of javascript functions which depend on some server-side constants (such as strings from my resources file). While I was developing them I kept the javascript code in the view header, so I could simply used server-side tags inside my javascript code, but now I'd like to move the javascript functions to a separate file.
I can't use a regular js file since that would not be interpreted by the server, making the server tags embedded there useless. I don't want to define variables in the page either since that seems way to awkward and error-prone.
What I've done instead is to create a new aspx file, put the javascript functions there and include that aspx file instead of a regular js file in my master template. It seems a bit unorthodox, but it seems to work fine.
Is there any downside to my approach that I have not taken into account? Or any better (less obscure) method?
Edit Bonus question: Should I use a DOCTYPE inside the included scripts file? After all, the scripts file is included by a script tag already. I tried to mimic regular js files, so I did not specify any DOCTYPE.
The file extension, be it js, aspx, ashx, bla, foo, whatever isn't all that important. If you have server side generated javascript that isn't specific to a page, then creating an ASPX page to render the javascript should be okay.
We'll often use HTTP handlers to generate dynamic javvascript in our systems. We also make sure to set the response headers to text/javascript to let the client browser know that we are sending back javascript.
Using a view, with MVC's templating capabilities is a great way to accomplish this. It is easy to maintain, well understood and gets the job done fast. The only real trick is to get it to serve the correct content-type. Doing something like this:
public ActionResult ConfigurationSettings()
{
Response.ContentType = "text/javascript";
return View(Configuration);
}
Actually gets you text/html content type. The trick is to make sure you get the last word, add this to your controller:
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
base.Execute(requestContext);
requestContext.HttpContext.Response.ContentType = "text/javascript";
}
And you will get the correct content type; ViewResult seems to force it to go text/html.
We use the ScriptDataBlock control from JsonFx to emit variables into the page itself. It acts like a dictionary where the key is the variable name (e.g. "MyNamespace.myVar") and the value is a normal C# value (including whole objects). The control emits appropriate namespace generation and type conversion to native JavaScript types. So in that sense it ends up not being "awkward" or "error prone":
myDataBlock["MyApp.myVar"] = myObject;
If you are doing an external file, then a generic .ashx handler is probably your best bet. It will be lighter than a whole aspx page and gives you pretty raw control over the output. Things you will want to be sure to do are to set the "Content-Type" response header to "text/javascript" (technically incorrect but most common MIME type) and for Internet Explorer which tends to not respect the Content-Type header, also set the "Content-Disposition" header to "inline;MyScriptName.js" so it knows the extension of the content.
Also, if these really are "constants" rather than runtime calculated data, you will want to set the "Expires" header to some future date to encourage the browser to cache the result. That will further reduce your server requests.
Edit: no DocType if you are creating JavaScript. DocType is only for markup.
You are using the MVC framework (your question is tagged as such) right? If so, you can create an action that returns a JavaScriptResult that will be executed on page when it loads:
public class JSController : Controller {
public ActionResult Headers() {
// create your variables here
return JavaScript("alert('hi');");
}
}
And then you can add it to your aspx/master page:
<script src="/JS/Headers" type="text/javascript"></script>