I have a file in the App_Code file which is some legacy code, that I use to control Login and sessions. Ideally, once I am logged in, I wish to update the ..Master.cs to fire a function to update the layout (some panels, become enabled etc).
I can access App_Code easily from Code Behind, though I'm unable to figure out how to do this the other way round.
MasterPage.master.cs (code behind)
public static class MasterPage : System.Web.UI.MasterPage{
...
public static void LogInCB{
//stufff
}
...
}
App_Code (something.cs)
public static string(){
//Master.LogInCB(); -tried
//System.Web.UI.MasterPage.LogInCB(); -tried
return something;
}
I'm happy to accept links to official MS documents on things about this.
The code-behind doesn't know what master page you are using. One way to fix this is to cast the Page.Master as your master page class. Once you do that, it will intellisense available functions:
YourNameSpace.MasterPage m = (YourNameSpace.MasterPage)Page.Master;
m.LogInCB();
Related
The project contains files of .aspx.cs , .aspx , .htm , .cs etc. As far as I understand, it is a web application project. I am working on a base page named PageBase.cs which includes features that all other pages would inherit from. I want to test how this page works and I am stuck.
There's no "Start Debug" nor "Run" options. The only one I get is "Attach to a Process". When I attached this .cs file to a process, VS shows that debug is ready but no outcomes are shown. I'm not even sure what outcomes I am expecting though so I can only stop debugging. The followings are the links I found in my research, hopefully they would be helpful in some way:
https://msdn.microsoft.com/en-us/library/3s68z0b3.aspx
https://msdn.microsoft.com/en-us/library/df5x06h3(v=vs.110).aspx
I know this question is trivial but I am totally new to .Net. Please help.
Since you've only created a class, you need to have a way to reach that code. Have one of your pages inherit from that class, and make sure that your custom class is wired into the Page Life cycle events properly (Page_Load, Init etc) depending on when you want the code to execute.
Assuming you set up the inheritance properly, and that your debugger is attached to the process, your breakpoints in the class will be hit when you access that page and hit the appropriate stages in the page lifecycle.
This is what I did for PageBase.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Test.Lib.Base
{
public class PageBase : System.Web.UI.Page
{
#region Method
protected override void OnInit(EventArgs e)
{
AutocompleteOff();
base.OnInit(e);
if (User.Identity.IsAuthenticated)
ViewStateUserKey = Session.SessionID;
}
protected override void AutocompleteOff()
{
Page.Form.Attributes.Add("autocomplete", "off");
}
#endregion
}
}
And then for other pages under test folder, (Body.aspx.cs for instance) I added PageBase as the following:
public partial class PostLogin : Lib.Base.PageBase
{
# Method
...
}
I want to create a modular ASP.NET application. Something like, I have a main application that is just some kind of module loader. It only have the one "Default.aspx" page. And, based on the loaded modules, this page will create a menu and links to the pages found in the modules.
I want the modules to be ASP.NET projects packed into dll. So, I want to drop that dll into the "Modules" folder of my main application, it will identify the module, and use reflection to load the modules, inspect them to find the pages, and build a menu from that.
What I've done so far:
In my solution, I have a "DummyModule" project. This project have only 3 pages. Nothing special about it.
And I have another project called "MainApp". Here is the "big deal".
In this project I have a "ModuleLoader" class. When the "LoadModules" method is called, it search for "dll" files in the "Modules" folders of my application. And, using reflection, load these modules. Foreach of these modules, still using reflection, it searches all "Page" types, and stores the names into a list.
On the "Page_Load" method of the "Default.aspx" page, it call de "ModuleLoader" class, gets all modules names and all pages names for each module, and build a menu from that. I created a hyperlink pattern, that have all the information I need to load the right page. That is : "/ModuleName/PageName".
I'm not using the "aspx" extension. OK, so far, so good.
Here is the tricky part.
I've created a HTTPModule called "PageLoaderModule". This modules intercepts all requests, so I can read the URL to identify wich page from wich module I have to load.
And that's exactly what I cannot do and I have no idea how to solve this.
What I'm doing:
public class PageLoaderModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
}
private void context_BeginRequest(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
if (Regex.IsMatch(application.Request.RawUrl, #"/.+/.+"))
{
var parts = application.Request.RawUrl.Split('/').Where(u => !string.IsNullOrWhiteSpace(u)).ToList();
IHttpHandler page = ModuleManager.GetPage(parts[0], parts[1]);
page.ProcessRequest(application.Context);
}
}
#endregion IHttpModule Members
}
The "GetPage" method, find the correct "Page" type in the specified assembly, create an instance and return the that Page instance.
But when I call the "ProcessRequest" method of the IHTTPHandler interface, it doesn't load the page.
It's possible to do that? Any thoughts?
Edit:
I've tried #Slavo suggestion.
While searching for an anwser, I've found and tried a similar solution, implementing my own VirtualPathProvider and VirtualFile.
It almost worked. The virtual path handle and load the correct page but, when the page is loaded, I got the following error in my browser:
Parser Error Message: Could not load type 'DummyModule.Pages.DummyPage3'.
Source Error:
Line 1: <% # Page Language="C#" AutoEventWireup="true" CodeBehind="DummyPage3.aspx.cs" Inherits="DummyModule.Pages.DummyPage3" %>
So, I don't know if I've done something wrong, or this isn't the solution I'm looking for. So, I tried other option.
I correctly marked the "Build Action" of the ".aspx" file as "Embedded Resource", so it can be accessible as a virtual path. But I still got the error above.
This looks like a case where you would want to write a VirtualPathProvider. This class lets you control the logic, which provides components to the compilation system.
When ASP.NET compiles a page to handle the request, by default it only uses the ASPX file and the code-behind. If you write a custom VirtualPathProvider, you will be able to tell it to do otherwise. So whenever ASP.NET needs to compile a page for a particular path to handle the request, your provider can extract it from an assembly.
Here is a helpful article: http://support.microsoft.com/kb/910441
You should handle the PostMapRequestHandler event in your module and set a custom IHttpHandler to the application.Current.Handler property. Here is an example.
I tried and tried, looked here, on Google and didn't find how to do it. I just try to write a simple user control (.ascx) to display different type of ads (which all of them are scripts). The problem is that usually it's a complicated scripts, so someone (here) suggested to save the scripts as .JS files and call them from the control (.ascx) file. The question: How do I do it? Tried lot of time and it's not work. I'm frustrated...
Can anyone pls give me an example code of how to do it?
Thanks a lot!
You can attach that "script file" to the page from the User Control using something like this:
(Taken from here in MSDN
public void Page_Load(Object sender, EventArgs e)
{
// Define the name, type and url of the client script on the page.
String csname = "ButtonClickScript";
String csurl = "~/script_include.js";
Type cstype = this.GetType();
// Get a ClientScriptManager reference from the Page class.
ClientScriptManager cs = Page.ClientScript;
// Check to see if the include script exists already.
if (!cs.IsClientScriptIncludeRegistered(cstype, csname))
{
cs.RegisterClientScriptInclude(cstype, csname, ResolveClientUrl(csurl));
}
}
This way you include your script file with the correct url into the page. Then will load on client side, and assuming you have written everything correctly, will perform as intended.
We have a nested layout for our various pages. For example:
Master.cshtml
<!DOCTYPE html>
<html>
<head>...</head>
<body>#RenderBody()<body>
</html>
Question.cshtml
<div>
... lot of stuff ...
#Html.Partial("Voting", Model.Votes)
</div>
<script type="text/javascript">
... some javascript ..
</script>
Voting.cshtml
<div>
... lot of stuff ...
</div>
<script type="text/javascript">
... some javascript ..
</script>
This all works fine, but I would like to push all of the JavaScript blocks to be rendered in the footer of the page, after all the content.
Is there a way I can define a magic directive in nested partials that can cause the various script tags to render in-order at the bottom of the page?
For example, could I create a magic helper that captures all the js blocks and then get the top level layout to render it:
Voting.cshtml
<div>
... lot of stuff ...
</div>
#appendJSToFooter{
<script type="text/javascript">
... some javascript ..
</script>
}
I came up with a relatively simple solution to this problem about a year ago by creating a helper to register scripts in the ViewContext.TempData. My initial implementation (which I am in the process of rethinking) just outputs links to the various referenced scripts. Not perfect but here's a walk-through of my current implementation.
On a partial I register the associated script file by name:
#Html.RegisterScript("BnjMatchyMatchy")
On the main page I then call a method to iterate the registered scripts:
#Html.RenderRegisteredScripts()
This is the current helper:
public static class JavaScriptHelper
{
private const string JAVASCRIPTKEY = "js";
public static void RegisterScript(this HtmlHelper helper, string script)
{
var jScripts = helper.ViewContext.TempData[JAVASCRIPTKEY]
as IList<string>; // TODO should probably be an IOrderedEnumerable
if (jScripts == null)
{
jScripts = new List<string>();
}
if (!jScripts.Contains(script))
{
jScripts.Add(script);
}
helper.ViewContext.TempData[JAVASCRIPTKEY] = jScripts;
}
public static MvcHtmlString RenderRegisteredScripts(this HtmlHelper helper)
{
var jScripts = helper.ViewContext.TempData[JAVASCRIPTKEY]
as IEnumerable<string>;
var result = String.Empty;
if (jScripts != null)
{
var root = UrlHelper.GenerateContentUrl("~/scripts/partials/",
helper.ViewContext.HttpContext);
result = jScripts.Aggregate("", (acc, fileName) =>
String.Format("<script src=\"{0}{1}.js\" " +
"type=\"text/javascript\"></script>\r\n", root, fileName));
}
return MvcHtmlString.Create(result);
}
}
As indicated by my TODO (I should get around to that) you could easily modify this to use an IOrderedEnumerable to guarantee order.
As I said not perfect and outputting a bunch of script src tags certainly creates some issues. I've been lurking as your discussion about the jQuery Tax has played out with Steve Souders, Stack Overflow, Twitter and your blog. At any rate, its inspired me to rework this helper to read the contents of the script files and then dump them to the rendered page in their own script tags rather than link tags. That change should help speed up page rendering.
Can you define a section at the bottom of the body element in your Master.cshtml file as follows:
#RenderSection("Footer", required: false)
Then in the individual .cshtml files you can use the following:
#section Footer {
<script type="text/javascript">
...
</script>
}
Rather than building some server-side infrastructure to handle a client-side concern, look at my answer to another question: https://stackoverflow.com/a/9198526/144604.
With RequireJS, http://requirejs.org, your scripts won't necessarily be at the bottom of the page, but they will be loaded asynchronously, which will help a lot with performance. Page rendering won't be halted while the scripts are executed. It also promotes writing Javascript as lots of small modules, and then provides a deployment tool to combine and minimize them when the site is published.
This is a bit hacky, but if your goal is to affect minimal changes on existing views (besides moving the rendered scripts), the way I've done it in the past (in Web Forms specifically but would also apply to MVC) was to override the TextWriter with one that pushes scripts to the bottom.
Basically you just write a TextWriter implementation and hook it up to your MVC base page that looks for <script src=" and captures the file name in an internal Queue, then when it starts to get Write calls for </body> it renders everything built up in its Queue. It could be done via regex but its probably pretty slow. This article shows an example of a TextWriter for moving ViewState but the same principal should apply.
In order to override for dependencies I then defined script files and dependent script files in my web.config, similar to this in situations where I needed ordering override:
<scriptWriter>
<add file="~/scripts/jquery.ui.js">
<add dependency="~/scripts/jquery.js" />
</add>
</scriptWriter>
Disclaimer Like I said, this is hacky, the better solution would be to use some sort of CommonJS / AMD like syntax in your partials (#Script.Require("~/scripts/jquery-ui.js")), basically you could write a function that if the master/layout page indicates its capturing script registration it can listen for all the child registrations, otherwise it can just output inline, so wouldn't hurt to just use it everywhere. Of course it may break intellisense.
So assuming some code in your master like:
#using(Script.Capture()) {
#RenderBody()
#Html.Partial("Partial")
}
And a partial of:
#Script.Require("~/scripts/jquery-ui.js")
Then you could just code something like this to handle it:
public class ScriptHelper : IDisposable
{
bool _capturing = false;
Queue<string> _list = new Queue<string>();
readonly ViewContext _ctx;
public ScriptHelper Capture()
{
_capturing = true;
return this;
}
public IHtmlString Require(string scriptFile)
{
_list.Enqueue(scriptFile);
if (!_capturing)
{
return Render();
}
return new HtmlString(String.Empty);
}
public IHtmlString Render()
{
IHtmlString scriptTags;
//TODO: handle dependencies, order scripts, remove duplicates
_list.Clear();
return scriptTags;
}
public void Dispose()
{
_capturing = false;
_ctx.Writer.Write(Render().ToHtmlString());
}
}
You may need to make the Queue ThreadStatic or use the HttpContext or something, but I think this gives the general idea.
If you have a situation where you cannot improve on the existing structure (ie. you have both MVC and old WebForms pages, javascript tags are included all over the place, etc...), you could have a look at some work I'm doing on an HTTP Module that does postprocessing on the output, much like mod_pagespeed does in Apache. Still early days though.
https://github.com/Teun/ResourceCombinator
If you can still choose to include your scripts in a predefined way, that is probably better.
Edit: the project mentioned by Sam (see comments) was indeed largely overlapping and far more mature. I deleted the ResourceCombinator project from GitHub.
I realise this ship has pretty much sailed but since I have some kind of solution (albeit not a perfect one) I thought I'd share it.
I wrote a blog post about approaches to script rendering that I use. In that I mentioned a library written by Michael J. Ryan that I have tweaked for my own purposes.
Using this it's possible to render a script anywhere using something like this:
#Html.AddClientScriptBlock("A script", #"
$(function() {
alert('Here');
});
")
And then trigger it's output in the layout page using this call just before the closing body tag:
#Html.ClientScriptBlocks()
You get no intellisense in Visual Studio using this approach. If I'm honest I don't really advise using this technique ; I'd rather have separate JS files for debug points and use HTML data attributes to drive any dynamic behaviour. But in case it is useful I thought I'd share it. Caveat emptor etc
Here's a list of relevant links:
My blog post
Michael J. Ryan's blog post
My helper on GitHub
I would suggest that you shouldn't be putting scripts directly onto your page.
Instead pull the JavaScript into a separate .js file and then reference it in the header.
I know it is not exactly what you are asking for, but I assume you are doing this for SEO purposes, and this should have the same effect as putting the script at the bottom of the page.
We use the ScriptRegistrar method from Telerik's (GPL open sourced) Asp.Net MVC library.
SImply include your javascript in any views/partials pages etc. as below. The Telerik library handles rendering all of this this at the bottom of the final outputted page.
<%
Html.Telerik().ScriptRegistrar()
.Scripts(scripts => scripts.AddSharedGroup('lightbox')
.OnDocumentReady(() => { %>
<%-- LIGHTBOX --%>
$('.images a').lightBox();
<% });%>
It also looks after grouping multiple css and js includes together into single requests.
http://www.telerik.com/products/aspnet-mvc.aspx
My humble option is to leave these kind of things to the professionals :-) Take a look at ControlJS, a lib to load your external javascript files without interrupting the page processing (async, defer, on demand etc). With ControlJS it doesn't mind "where" you will put the loading code, they will all load at the end or on demand.
This presentation of Steve Souders, the lib author, gives a good overview of the problem (and the solution).
Sam,
I wrote Portal for this very reason: http://nuget.org/packages/Portal You basically put a
#Html.PortalOut()
in your master / layout view and stuff HTML code in from views or partial views like this
#Html.PortalIn(#<text> $(function() { alert('Hi'); }); </text>)
You can use several "portals" by specifying a input and output key. Right now it adds code in the order it comes in (partials render first, from "top to bottom"). If you're in the same view you have to deal with the top to bottom restriction, i.e. you can't have an "out" portal before an "in" one, but this is not an issue when sending from views to layout. There are more examples in the Portal.cs file.
What about html Response Filter, that modifies your final html. Find all script tags before certain place and paste them at the end of body. It's also works for improving dataTables rendering, by marking them hidden initially and make js to show them.
No intervention or changes on actual code needed.
For example, if I have a page located in Views/Home/Index.aspx and a JavaScript file located in Views/Home/Index.js, how do you reference this on the aspx page?
The example below doesn't work even though the compiler says the path is correct
<script src="Index.js" type="text/javascript"></script>
The exact same issue has been posted here in more detail:
http://forums.asp.net/p/1319380/2619991.aspx
If this is not currently possible, will it be in the future? If not, how is everyone managing their javascript resources for large Asp.net MVC projects? Do you just create a folder structure in the Content folder that mirrors your View folder structure? YUCK!
You can use the VirtualPathUtility.ToAbsolute method like below to convert the app relative url of the .js file to an absolute one that can be written to the page:
<script type="text/javascript" src="<%=VirtualPathUtility.ToAbsolute("~/Views/Home/Index.js") %>"></script>
You should have separated folder structure for scripts. For example JavaScript folder under application root. Storing js files with views is not only affects you with path resolving issues but also affects security and permissions thins. Also it's much more easier later to embed JS files as assembly resources if you will decide to deploy some of your application parts separately in future when they are stored in dedicated subfolder.
For shared javascript resources using the Content folder makes sense. The issue was I was specifically trying to solve was aspx page specific javascript that would never be reused.
I think what I will just have to do is put the aspx page specific javascript right onto the page itself and keep the shared js resources in the Content folder.
Here's a nice extension method for HtmlHelper:
public static class JavaScriptExtensions
{
public static string JavaScript(this HtmlHelper html, string source)
{
TagBuilder tagBuilder = new TagBuilder("script");
tagBuilder.Attributes.Add("type", "text/javascript");
tagBuilder.Attributes.Add("src", VirtualPathUtility.ToAbsolute(source));
return tagBuilder.ToString(TagRenderMode.Normal);
}
}
Use it like this:
<%=Html.JavaScript("~/Content/MicrosoftAjax.js")%>
If you re-route your pages to a custom RouteHandler, you can check for existence of files before handling the RequestContext to the MvcHandler class.
Example (not complete):
public class RouteHandler : IRouteHandler
{
public IHttpHandler
GetHttpHandler(RequestContext requestContext)
{
var request = requestContext.HttpContext.Request;
// Here you should probably make the 'Views' directory appear in the correct place.
var path = request.MapPath(request.Path);
if(File.Exists(path)) {
// This is internal, you probably should make your own version.
return new StaticFileHandler(requestContext);
}
else {
return new MvcHandler(requestContext);
}
}
}