I've got some experience with ASP.NET MVC, but only with a web application where the content of the master page doesn't change. I've been wondering how you would handle a site where for example the menu (which is on the master page) is loaded from a database. Where would you load this data?
In WebForms, you would load the menu in the code-behind of the master page, or have the menu as a user control and do the loading in the code-behind of that. But where is this done in MVC?
Do you create a class that inherits from Controller that you use for all your Controllers and let that load the menu on every Action invocation (I don't know if that's possible, but it seems likely)?
Or do you create a utility method that you call in every Action where you want it (because some Actions may only return a partial view that won't reload the menu), which - while not disastrous - seems a little tiresome.
Or would you sin against MVC and just load it in the master page's code-behind?
What's the best approach to this (of course not limited to my solutions)?
ActionFilters are used to intercept a request and do some processing. You could use them.
Is it a sin against the MVC pattern?
You are breaking the MVC pattern to some extent. But the higher level point is: does it provide much more value if you force yourself not to break it? I don't think that puts you in much trouble, so keep simplicity and maintainability in mind and choose the way you'd do it in your specific situation.
I would create a model type that the master page gets its data from. Then derive the page's model type from the MasterModel.
The controller populates an instance of the PageModel, and the master page picks it up.
(Alternatively the master page's model could be an interface implemented by the pages' models.)
NB. In MVC CTPs doing this required some intermediate classes to override type matching to get to base class data. I don't know if this has been fixed in RTM.
The ASP.NET MVC Futures assembly (more info here) contains an extension method that lets you do this in your master page:
<% Html.RenderAction<NavigationController>(c => c.Show()); %>
You need to reference the assembly and add the "Microsoft.Web.Mvc" namespace in your web.config file for this to work.
Related
I have a problem how to reuse the webforms masterpage in mvc view. The project I am working on is an existing website where I have to change a single module to MVC. I have implemented successfully the routing logic but I am struggling with reusability of masterpages. I read this question here but it does not solve my problem because existing pages inherit from XClass which by itself inherits from System.Web.UI.Page. The same logic happens to be for mastepages. The reason is because the XClass makes a lot of logic in background - like dealing with session values and validating user data etc etc. Is there any way how can I use those masterpages within view in MVC? I tried the solution given in question above but while debugging there is a statement XClass xpage = Page as XClass in Page_Load event which gives me the value null and throws exception since in MVC we do not have Page_Load event etc... As I have read in other places they recommend to create a new masterpage for MVC but in my condition it would be very difficult. Any suggestion how to solve this problem ?
I am reading this article here but yet I am not clear how to implement it...
Here I have an image attached for masterpage in webforms.
My understanding is that when a component or custom control is defined in an aspx page using the <%Register%> tag, it is declared in an auto-generated designer.cs (C#) file by the compiler. If this custom control is never used in the aspx page, does this still happen in the designer.cs file?
Assuming the control is used in the aspx page, what mechanism then instantiates this control, how is it new-ed up behind the scenes? The designer file only declares it. Thanks much, and if there are good articles out there discussing this I’d be happy to read them.
This may not answer all your questions, but some of them. Rick Strahl wrote a great article a while back on compilation and deployment that describes how it works:
Compilation and Deployment in ASP.NET 2.0
I added part of the article that I think relates most to your question below:
Referencing other Pages and Controls
Remember that page and control compilation happens on a per directory basis! So referencing other pages and controls becomes a little more tricky for ASP.NET 2.0, because you can no longer assume that a CodeBeside class from another page or control is available in the current assembly. At best all pages and controls in the same directory end up in the same assembly, at worst each page or control gets its own assembly and they know nothing about each other.
If you need to reference another page from a control or another page you need to explicitly import it with the #Reference directive. Again this is different than ASP.NET 1.1 where all CodeBehind classes were immediately available to your entire Web application. In ASP.NET 2.0 an explicit assembly reference is required to load it.
Assume for a minute that you have the DataEntry.aspx page I showed earlier and you want to create a second page that uses the same CodeBeside class so you can reuse the page logic, but change the page layout in DataEntry2.aspx by changing a few colors and moving around the controls of the page. In essence you want to have two ASPX pages reference the same CodeBeside file.
Here’s how to do this:
<%# Reference Page="~/DataEntry.aspx" %>
<%# Page Language="C#" AutoEventWireup="true" Inherits="DataEntry" %>
I’m leaving out the CodeFile attribute reference the CodeBeside class of the DataEntry page, and add the #Reference tag to the page to force the CodeBeside class to be imported.
The same is true with any User Control definitions. To import a user control you need to use the #Register tag, which imports the assembly that the control lives in. ASP.NET is smart during compilation and figures out exactly where related assemblies live based on how the project is compiled. If the control or page lives in the same assembly no reference is actually added. But if it is external – in another directory for example, then the assembly reference is added.
Referencing problems
If you can explicitly reference other pages and controls in your markup pages, then all works well and as expected. But if you dynamically load controls or reference pages dynamically in your code, things get a lot more complicated.
The most common problem I run into is dynamic loading of controls. In ASP.NET 1.x you might have run code like this for dynamically loading controls into a page:
public partial class DynamicControlLoading : System.Web.UI.Page
{
protected CustomUserControl MessageDisplay = null;
protected void Page_Load(object sender, EventArgs e)
{
MessageDisplay = this.LoadControl( "~/UserControls/CustomUserControl.ascx") as CustomUserControl;
this.Controls.Add(MessageDisplay);
}
protected void btnSay_Click(object sender, EventArgs e)
{
this.MessageDisplay.ShowMessage(this.txtMessage.Text);
}
}
CustomUserControl in this case is a simple User Control that lives in another directory and is loaded dynamically at runtime. Further assume that you truly dynamically want to load this control so you may have a choice of several controls, or the end-user might even create a custom control that gets dropped into place instead.
If you run the code above in ASP.NET 2.0 it will likely fail. I say likely because there are some inconsistencies that will sometimes pick up control references automatically, for example if the user control lives in the same directory and gets compiled into the same assembly as the page, or if another page has the control referenced.
It should and usually will fail. Why? Because ASP.NET compiles on a directory level and the CustomUserControl lives in a separate directory and so goes into a separate assembly. It’s not visible to page class to get a strongly typed reference. Intellisense will show a big, fat and red exclamation point or nothing at all for the MessageDisplay control. When you run the page it will bomb.
You can reference the control as the Control type of course, but if you need to access any custom properties on the user control beyond Control properties you can’t unless you resort to Reflection. As far as I know there’s no way to add a reference to another user control or page programmatically because the reference needs to be available way earlier at compile time before your code ever runs.
Alternatives are to not load controls dynamically or at least provide some mechanism to load up any user controls beforehand on a page with the appropriate #Register tags. But that’s not always possible. The other option is to create a user control base class in APP_CODE and expose the public interface there. The main problem with this is that this base class will not have access to any internal controls of the user control and so the base class would have to use FindControl to reference any embedded controls. So this is inefficient as hell, and cumbersome to boot.
I’ve run into similar situations with inheritance scenarios. For example, inheriting one master page off another’s CodeBeside class. All works well, but the ASP.NET compiler complains that the Profile object is being overridden illegally (a compiler warning). Running with the inherited master page works, but there are quirks. User Controls added to the master page often fail with type conflicts as ASP.NET treats the user control added to the base page as a different type than the user control added to the second page.
It’s inconsistencies like these that deal with referencing other types that have made me waste an incredible amount of time, thinking I had something fixed only to find out later that it didn’t actually work consistently when I changed a completely different page. Worse you have to really understand the model to get your head around what might be wrong.
Bottom line: The overall ASP.NET 2.0 compilation model is internally complex. Most of the time you don’t need to understand it, but when you run into these boundary scenarios, you really DO have to understand what goes on behind the scenes to be able to work around the quirks.
Can we call a Method that exist on a aspx page through Web Services.
It is like Reflection that method exists in dll form , but is it possible to call method from Aspx page if yes then please tell me how is it?:confused:
The code-behind class in an ASPX page is just a class, so technically there is nothing stopping you from doing that. However, it strikes me as a really bad idea from code design point of view.
The code in an ASPX page class should be related only to that page. If you need the same functionality elsewhere, move the code for that functionality to some other place (such as a class library that can be called from both places).
You should extract common functionality to a class of its own - this way you can call the code from both the web page and the web service and keep them decoupled from each other.
Results r = (Results)Page.LoadControl("Results.ascx");
In my Page i cannot access USER CONTROL CLASS(Results)
Gives error.I cannot resolve.
No namespaces at all.
Even i cannot access other aspx class in same folder's other page also..
Can you please help me.
ASP.NET compiles pages in batches, and there's no way you can be sure that your currect page is compiled with the Results page. You need to define an interface that you store in App_Code (which all pages are compiled with), implement this interface in your Results page, and use the interface to access whatever you're trying to do.
You could move logic from your Results page into your domain, or into a helper class, instead of keeping it in the view (the page).
I am working on an ASP.NET MVC application that contains a header and menu on each page. The menu and header are dynamic. In other words, the menu items and header information are determined at runtime.
My initial thought is to build a base Controller from which all other controllers derive. In the base controller, I will obtain the menu and header data and insert the required information into the ViewData. Finally, I will use a ViewUserControl to display the header and menu through a master page template.
So, I'm trying to determine the best practice for building such functionality. Also, if this is the recommended approach, which method should I override (I'm guessing Execute) when obtaining the data for insertion into the ViewData.
I'm sure this is a common scenario, so any advice/best-practices would be appreciated! Thanks in advance!
EDIT:
I did find the following resources after posting this (of course), but any additional anecdotes would be awesome!
http://www.singingeels.com/Blogs/Nullable/2008/08/14/How_to_Handle_Side_Content_in_ASPNET_MVC.aspx
How do you use usercontrols in asp.net mvc that display an "island" of data?
Depends on where your information is coming from. We have standard view data that we use to generate some of the information we have on screen that we create in just this fashion. It works well and is easily maintained. We override the View method to implement strongly typed view names and use this information to retrieve some of the data that the master page requires as well.
You could write a helper extension to render the header/menu
That way you could have it show in different places in the view should you need to, but only one place for maintenance.
public static HtmlString MainMenu(this HtmlHelper helper)
Use a base controller class to implement generell filter methods. The controller class implements some filter interfaces IActionFilter, IAuthorizationFilter, IExceptionFilter and IResultFilter which are usefull to implement some common behavior for all controllers.
If the menu data is the same on all pages but different for each unique user.
Generate the menudata in an OnAuthorization or Initialize method of your controller base class. First will be called on authorization. Initialize will be called before every action method. You have access to ViewData Context. Generate the menudata there.
Put the view content for menu and header into the master page and access generated ViewData there.
I tackled a similar design challenge a couple months ago - implementing a breadcrumb feature that changes as user navigates from page to page.
I overrided the OnActionExecuting method to gather the breadcrumbs and store them in ViewData (I use the name of the action as the breadCrumb of the view). Then I updated the Master page to include a user control that takes the ViewData and renders the breadcrumbs.
One thing to be aware is that if you were using the default ASP.NET MVC error handling attribute [HandleError] and your error page is using the same Master page that attempts to read the ViewData, you will soon find out that you can't access ViewData from your error page and it will raise an exception. Depending on whether you need the ViewData for failure scenarios, the viable solution is to use a separate Master page or do this: How do I pass ViewData to a HandleError View?
I'll answer your question with another question. Will the base controller have to determine what type it really is in order to generate the proper menu data? If so, then you're defeating the purpose of polymorphism and the code to generate the data should go in each controller, perhaps in OnActionExecuting if the menu is the same for all actions. Pushing it back down into a parent class seems likely to end up with some switch statement in the parent class doing what each derived controller really ought to take care of.