Setup:
- CustomViewEngine
- CustomController Base
- CustomViewPage Base (in this base, a new property is added "MyCustomProperty")
Problem:
When a view is strongly typed such as: <@ Page Inherits="CustomViewPage<MyCustomObject" MyCustomProperty="Hello">
, I get a compiler "Parser" error stating that MyCustomProperty is not a public property of System.Web.Mvc.ViewPage
I have done numerous trial and errors (see below) to see whats causing this error and have come to the following conclusions:
- The error only occurs when I declare "MyCustomProperty" or any other property in the @Page directive of the view.
- The error will always display "System.Web.Mvc.ViewPage" rather than the declared inherits=".." class.
Best Answer
Update: Looks like Technitium found another way to do this that looks much easier, at least on newer versions of ASP.NET MVC. (copied his comment below)
Original answer below:
OK, I solved this. Was a fascinating exercise, and the solution is non-trivial but not too hard once you get it working the first time.
Here's the underlying issue: the ASP.NET page parser does not support generics as a page type.
The way ASP.NET MVC worked around this was by fooling the underlying page parser into thinking that the page is not generic. They did this by building a custom PageParserFilter and a custom FileLevelPageControlBuilder. The parser filter looks for a generic type, and if it finds one, swaps it out for the non-generic ViewPage type so that the ASP.NET parser doesn't choke. Then, much later in the page compilation lifecycle, their custom page builder class swaps the generic type back in.
This works because the generic ViewPage type derives from the non-generic ViewPage, and all the interesting properties that are set in a @Page directive exist on the (non-generic) base class. So what's really happening when properties are set in the @Page directive is that those property names are being validated against the non-generic ViewPage base class.
Anyway, this works great in most cases, but not in yours because they hardcode ViewPage as the non-generic base type in their page filter implementation and don't provide an easy way to change it. This is why you kept seeing ViewPage in your error message, since the error happens in between when ASP.NET swaps in the ViewPage placeholder and when it swaps back the generic ViewPage right before compilation.
The fix is to create your own version of the following:
Then you need to change the web.config in your views directory (not the main app's web.config) to use these new types instead of MVC's default ones.
I've enclosed some code samples illustrating how this works. Many thanks to Phil Haack's article to help me understand this, although I had to do a lot of poking around the MVC and ASP.NET source code too to really understand it.
First, I'll start with the web.config changes needed in your web.config:
Now, here's the page parser filter (#1 above):
Here's the page builder class (#2 above):
And here's the custom view page classes: the non-generic base (#3 above) and the generic derived class (#4 above):
And here are the corresponding classes for user controls (#5 above) :
Finally, here's a sample View which shows this in action: