Overriding a master page function in ASP.net - c#

In my master page I have the line
<div class ="breadcrumbs" runat="server"><%= SitePath()%></div>
SitePath() involks a custom class that builds navigation elements based on the combination of the static sitemap and dynamically generated pages. It returns the html for a custom breadcrumb navigation element.
Here is the SitePath() code from my code behind
public string SitePath()
{
BreadCrumbNav breadNav = new BreadCrumbNav();
breadNav.divClass = ".dv";
breadNav.homeTitle = "ABC Home";
return breadNav.Build();
}
I'd like to be able to override this from my dynamic pages so I can add to the path. For Example...
public override string SitePath()
{
BreadCrumbNav breadNav = new BreadCrumbNav();
breadNav.divClass = ".dv";
breadNav.homeTitle = "ABC Home";
breadNav.AddPage("Cooking Equipment", "PathyGoodness/Cooking+Equipment.ASPX");
breadNav.AddPage("Toasters", "PathyGoodness/Cooking+Equipment/Toasters.ASPX");
return breadNav.Build();
}
Is there a way to bring the master page methods into scope so I can override them- or do I need to go about this in a different way? It feels like I'm missing something really obvious, but I seem to be stuck.
Thanks for your help!

Since the Master page is not actually inherited, but rather used as a template, this is not directly possible.
A few other approaches you could try:
Firstly, put this in a ContentPlaceholder and override it in the ASPX markup of your individual pages only where necessary.
A more complicated approach would be to create an interface for all your pages to inherit from that guarantees they have a SitePath() method. Call that method on the page instead, from the Master, and in those pages that do not "override" the behavior, simply call the Master. In others, add your specific implementation to that method, and voila! (I suppose you could also use an abstract BasePage-type class to call the Master from that method)

Generally im against putting page/context specific methods in the masterpage class. Why? Because, as Josh mentioned, Masterpages is just templates. You'll never inherit from a masterpage and you can't show a masterpage without also having access to a Page object. Therefor it makes more sense to put such methods in the Page class.

A much simpler way is to reference a master control directly from your child page.
In your master page, replace the sitepath() function with a literal control like so:
<div class ="breadcrumbs" runat="server"><asp:Literal id="litSitePath" runat="server" /></div>
then in the code for the master page use this:
BreadCrumbNav breadNav = new BreadCrumbNav();
breadNav.divClass = ".dv";
breadNav.homeTitle = "ABC Home";
litSitePath.Text = breadNav.Build();
and in the child page you can override this by using the Master.FindControl method like this:
BreadCrumbNav breadNav = new BreadCrumbNav();
breadNav.divClass = ".dv";
breadNav.homeTitle = "ABC Home";
breadNav.AddPage("Cooking Equipment", "PathyGoodness/Cooking+Equipment.ASPX");
breadNav.AddPage("Toasters", "PathyGoodness/Cooking+Equipment/Toasters.ASPX");
Literal litSitePath = (Literal)Master.FindControl("litSitePath");
litSitePath.Text = breadNav.Build();
Easy peasy and no messing around with eventhandlers or interfaces or anything like that.

Related

Is it possible to restrict an instance of a pagetype in EpiServer?

Under Start I want the user to only have the possibility to create one instance of my EmailTemplatePage pagetype. Is it possible?
A simple solution could be that you create the single page, and then set "Available in edit mode" to false on the page type. Then it will not be possible for editors to create another page (but they can still make a copy of the existing page).
If your requirement is that an editor should be able to create just one page of a specific page type in a specific part of the page tree, you would need to create a custom validation when the page is saved.
Something like this: https://blog.wsol.com/improving-episervers-content-editing-experience-through-validation
You can use the AvailableContentTypes attribute on your start page type:
[ContentType]
[AvailableContentTypes(Availability.Specific, Include = new [] { typeof(EmailTemplatePage) })]
public class StartPage : PageData
{
}

Pass Page Name to function (ASP.Net)

I have a routine which finds all the controls on a page, which I then apply some changes to.
The routine works quite well but I will have to add the chunk of code to every master page and content page to get my results.
I would like to create a single sub routine (which will be in a module) which when called from each page will carry out the work. The only thing I think I need to do to do to make this work is to be able to pass the page name the controls are on to the sub routine to run my routine and have the module make the changes I require to the page I pass to it.
I am sure this is an easy process, but for the life of me I can’t work it out. Can someone help me on how to pass the page name so the routine will apply the changes to that page?
I am using asp.net in Visual Studio 2010, and the code behind is vb.net.
My routine is written in the code behind, not java or aspx. I don’t care if the answer is in C# or vb.net as I can convert if required.
Thanks in advance
Chris
You can send this.Page.UniqueID value as a parameter to the subroutine when you call it from the module.
If you want the current page's class name use:
this.GetType().Name;
If your page inherits from another page, you would use:
this.GetType().BaseType.Name;
What I would do is create a base page that all your pages inherit from and put your FindAllControls() method there.
Here's an example of what that might look like:
using System.Web.UI;
public abstract class BasePage : Page
{
public void FindAllControls()
{
var currentClassName = this.GetType().BaseType.Name;
// Do stuff.
}
}
Then, for example, if you had a Login page, it would look like this:
using System;
public partial class Login : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
this.FindAllControls();
}
}

How can I find the (file) name of the current page in ASP.NET?

How can I find the name of (default.aspx ) current page or web control in the code behind?
I want to write a superclass that uses this name.
You mean that you want to find the original filename of the object that is currently executed? I.e., from inside a control MyControl you want to retrieve MyControlOnDisk.ascx? In general, this information is lost upon compiling, and moreover, many pages and controls are built on partial classes and the filenames they're from are compiled into a single assembly.
For a page, you can use the following, but only if the page is not internally redirected, is not instantiated as a class from another page, it is not a master page and you're not inside a static method:
string currentPageFileName = new FileInfo(this.Request.Url.LocalPath).Name;
In the case of a control, it is generally not possible as far as I know (it is compiled away), but perhaps someone can shed some light on this.
"i want to write a superclass that use this name "
I assume you mean to write a subclass? If you write a superclass you just create a virtual method and have it implemented in your subclass (the page). If you mean to create a subclass, you can take the classname of the page, which looks like this:
// current page
public partial class MyLovelyPage : System.Web.UI.UserControl
and use it like this to derive from it:
public partial class NewDerivedPage : MyLovelyPage
I would recommend an alternative:
Server.MapPath(Page.AppRelativeVirtualPath)
This works with ASP.Net to get the full path and filename for the current page.
Request.ServerVariables["SCRIPT_NAME"]
if you not use Routing :
string sPath = HttpContext.Current.Request.Url.AbsolutePath;
string[] strarry = sPath.Split('/');
int lengh = strarry.Length;
string sRet = strarry[lengh - 1];

ASP.Net and GetType()

I want to get a type of a "BasePage" object that I am creating. Every Page object is based off BasePage. For instance, I have a Login.aspx and in my code-behind and a class that has a method Display:
Display(BasePage page) {
ResourceManager manager = new ResourceManager(page.GetType());
}
In my project structure I have a default resource file and a psuedo-translation resource file. If I set try something like this:
Display(BasePage page) {
ResourceManager manager = new ResourceManager(typeof(Login));
}
it returns the translated page. After some research I found that page.GetType().ToString() returned something to the effect of "ASP_login.aspx" How can I get the actual code behind class type, such that I get an object of type "Login" that is derived from "BasePage"?
Thanks in advance!
If your code-beside looks like this:
public partial class _Login : BasePage
{ /* ... */
}
Then you would get the Type object for it with typeof(_Login). To get the type dynamically, you can find it recursively:
Type GetCodeBehindType()
{ return getCodeBehindTypeRecursive(this.GetType());
}
Type getCodeBehindTypeRecursive(Type t)
{ var baseType = t.BaseType;
if (baseType == typeof(BasePage)) return t;
else return getCodeBehindTypeRecursive(baseType);
}
After some additional research I found that if I call Page.GetType().BaseType it returns the code-behind type of the Aspx page.
page.GetType().BaseType, it has been said before, but let me elaborate as to why.
Aspx pages inherit from their code-behind pages, meaning that the inheritance hierarchy looks like this:
...
Page
BasePage
Login
ASP_Login
Where the top is the parent and the bottom is the child.
This allows your code behind to be accessible from the aspx page, without requiring all of the generated code related to your actual aspx page to be copied into the base class page.
It depends where you're calling Display() from. If you're calling it from the ASPX, then you'llse "ASP_login.aspx". If you're calling it from the code-behind (i.e. the Page_Load() method) you should get the Login page type.
Instead of passing the Page in, you might consider just using the Page property (i.e. this.Page.GetType()) which should always be the current page/codebehind type, if I recall correctly.
I should also make the point that you might consider moving this sort of stuff out of the ASPX/codebehind and into some sort of service. It's generally a good idea to minimize the amount of things you do in a code behind and, instead, push logic into a presenter class and follow the MVP pattern for ASP.NET Web Forms development.

Can I stop .NET eating IDs?

I'm an Information Architect and JavaScript developer by trade nowadays, but recently I've been getting back into back-end coding again. And, whilst trying to get an HTML prototype integrated and working with our C#-based CMS, I've come to blows with our programmers over the HTML ID attributes being arbitrarily rewritten by .NET for form elements.
I can understand the code-behind reasoning for .NET changing IDs, but the fact you can no longer use IDs when trying to develop e.g. jQuery enhanced interfaces is causing some friction. What can I do to work around this?
I've tried using the class attribute instead, but that's really crappy, not what it's meant for and doesn't get around that problem of .NET effectively changing rendered source on the fly. It also means that CSS is less useful now and less efficient to create and maintain.
Any tips or advice greatly appreciated--anything for a few less sleepless nights...
The short answer is no, with webforms the id can always be rewritten depending on the nesting of the element. You can get access to the id through the ClientID property, so you could set the ids into variables in a script at the end of the page/control then put them into jQuery.
something like this:
<asp:button id="ImAButton" runat="server">Click Me</asp:button>
<script type="text/javascript">
var buttonId = "<%=ImAButton.ClientId%>";
$("#"+buttonId).bind('click', function() { alert('hi); });
</script>
It's a hack I know, but it will work.
(I should note for the un-initiated, I'm using the Prototype $ get by id method there)
One method is to override the ID's manually:
public override string UniqueID
{
get { return this.ID; }
}
public override string ClientID
{
get { return this.ID; }
}
Rick Strahl wrote a blog post with some more information on that approach.
Look at ASP.Net MVC - it addresses the over-kill object hierarchies that ASP.Net generates by default.
This site is written in MVC (I think) - look at it's structure. Were I working on a new project right now I would consider it first
If you're stuck with basic ASP.Net then be careful overriding the ClientID and UniqueID - it tends to break many web controls.
The best way I've found is to pass the unreadable ClientID out to the Javascript.
You can extend .net controls and make them return actual id's when related properties are called.
ClientID is the id attribute and UniqueID is the name attribute of html elements. So when you create a textbox like the following and using this instead of the textbox in framework, you make id and name attributes render as the same as the server-side id.
public class MyTextBox : TextBox
{
public override string ClientID { get { return ID; } }
public override string UniqueID { get { return ID; } }
}
To use this new user control, basically register this control as you would do for a custom user control (you can do is in web.config so you won't have to do it in all your pages):
<%# Register Assembly="MyLibrary" NameSpace="MyLibrary.WebControls" TagPrefix="MyPrefix" %>
And use it like you would use a text box:
<MyPrefix:MyTextBox ID="sampleTextBox" runat="server" />
Personally, I use a set of methods I have developed for bridging the server-side ASP.NET "magic" (I have yet to use the MS MVC stuff yet) and my client-side code because of the munging of the IDs that happens. Here is just one that may or may not prove useful:
public void RegisterControlClientID(Control control)
{
string variableDeclaration = string.Format("var {0} = \"{1}\";", control.ID, control.ClientID);
ClientScript.RegisterClientScriptBlock(GetType(), control.ID, variableDeclaration, true);
}
So, in your server-side code you simply call this and pass in the instance of a control for which you want to use a friendlier name for. In other words, during design time, you may have a textbox with the ID of "m_SomeTextBox" and you want to be able to write your JavaScript using that same name - you would simply call this method in your server-side code:
RegisterControlClientID(m_SomeTextBox);
And then on the client the following is rendered:
var m_SomeTextBox = "ctl00_m_ContentPlaceHolder_m_SomeTextBox";
That way all of your JavaScript code can be fairly ignorant of what ASP.NET decides to name the variable. Granted, there are some caveats to this, such as when you have multiple instances of a control on a page (because of using multiple instances of user controls that all have an instance of m_SomeTextBox within them, for example), but generally this method may be useful for your most basic needs.
What I usually do is create a general function that receives the name of the field. It adds the usual "asp.net" prefix and returns the object.
var elemPrefix = 'ctl00-ContentPlaceHolder-'; //replace the dashes for underscores
var o = function(name)
{
return document.getElementById(elemPrefix + name)
}
With that you can use this kind of calls in jQuery
$(o('buttonId')).bind('click', function() { alert('hi); });
You definitely don't want to hard-code the asp.net-generated ID into your CSS, because it can change if you rearrange things on your page in such a way that your control tree changes.
You're right that CSS IDs have their place, so I would ignore the suggestions to just use classes.
The various javascript hacks described here are overkill for a small problem. So is inheriting from a class and overriding the ID property. And it's certainly not helpful to suggest switching to MVC when all you want to do is refactor some CSS.
Just have separate divs and spans that you target with CSS. Don't target the ASP.NET controls directly if you want to use IDs.
<div id="DataGridContainer">
<asp:datagrid runat=server id="DataGrid" >
......
<asp:datagrid>
</div>
If you're using jQuery then you have loads of CSS selectors and jQuery custome selectors at your disposal to target elements on your page. So rather than picking out a submit button by it's id, you could do something like:
$('fieldset > input[type="submit"]').click(function() {...});
I can see how the .NET system feels less intuitive, but give it a chance. In my experience it actually ends up creating cleaner code. Sure
<asp:button id="ImAButton" runat="server">Click Me</asp:button>
<script type="text/javascript">
var buttonId = <%=ImAButton.ClientId%>
$(buttonId).bind('click', function() { alert('hi); });
</script>
works fine. But this is suffers from not being modular. What you really want is something like this:
<script type="text/javascript">
function MakeAClick(inid)
{
$(inid).bind('click', function() { alert('hi); });
}
</script>
and then later with your code on the java side or the C# side you call MakeAClick. Of course on the C# side it makes more sense, you just ClientID in there.
Maybe this is the real problem with the code you are reviewing.
A much better approach would be to use the ClientIDMode and set it to static. You can even set it for a specific page or globally in the web.config file. Then you never have to deal with this issue again and your JQuery is much cleaner.
Top of page:
<%# Page Title="" ClientIDMode="Static" Language="C#" CodeBehind="..." Inherits="WebApplication1.WebForm2" %>
On control only:
<asp:Panel runat="server" ClientIDMode="Static"></asp:Panel>

Categories