Search code examples
asp.net-mvcasp.net-mvc-2asp.net-mvc-viewsasp.net-mvc-templates

How do I put a strongly-typed-view WebForms .aspx template in a nonstandard location in ASP.NET MVC2?


So, I personally think this is sort of whack.

I put a .aspx template in a nonstandard location. In this example, it has a virtual path of ~/Content/Sites/magical/Index.aspx.

I then created my own view engine as a test, which extends WebFormsViewEngine:


public class MagicalWebFormsViewEngine : WebFormViewEngine
{
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        string viewTemplatePath = "~/Content/Sites/magical/" + viewName + ".aspx";
        string masterTemplatePath = string.Empty;
        return new ViewEngineResult(
            this.CreateView(controllerContext, viewTemplatePath, masterTemplatePath),
            this
        );
    }
}

The template looks like this:


<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Plain.Master" Inherits="System.Web.Mvc.ViewPage<MySoln.Client.Presentation.MyPresenter>" %>
...
<%: Model.SomePresenterSpecificMember %>

If I leave the strongly-typed declaration in the Inherits attribute of the Page declaration, I get the following exception:

Parser Error Message: Could not load type 'System.Web.Mvc.ViewPage<MySoln.Client.Presentation.MyPresenter>'.

However, if I change the template to use a weakly-typed page model, and instead use a cast on the Model member in the template itself, it works:


<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Plain.Master" Inherits="System.Web.Mvc.ViewPage" %>
...
<% var omg = (MySoln.Client.Presentation.MyPresenter) Model; %>
<%: omg.SomePresenterSpecificMember %>

So, my question is, why does the former barf and the latter work? I'd rather not cast Model to one of my presenter types in a tag at the top of every template.

Thanks!


Solution

  • Just make sure that you have the following web.config file at the root of your custom view engine path:

    <?xml version="1.0"?>
    
    <configuration>
      <system.web>
        <httpHandlers>
          <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
        </httpHandlers>
    
        <!--
            Enabling request validation in view pages would cause validation to occur
            after the input has already been processed by the controller. By default
            MVC performs request validation before a controller processes the input.
            To change this behavior apply the ValidateInputAttribute to a
            controller or action.
        -->
        <pages
            validateRequest="false"
            pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
            pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
            userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
          <controls>
            <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
          </controls>
        </pages>
      </system.web>
    
      <system.webServer>
        <validation validateIntegratedModeConfiguration="false" />
    
        <handlers>
          <remove name="BlockViewHandler"/>
          <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
        </handlers>
      </system.webServer>
    </configuration>
    

    You could copy-paste the web.config file automatically generated by the default template and located in ~/views/web.config into ~/content/web.config.

    Basically the important part is :

    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, ..."