Page view counter

MasterPage<TViewData> Problem

Last post 01-18-2008 4:33 PM by Michael Jenkinson. 6 replies.

Sort Posts:

  • MasterPage<TViewData> Problem

    12-17-2007, 7:53 AM

    I was hoping to be able to pass strongly-typed ViewData to master pages using an interface as follows:

    1. Define an Interface for the MasterPage ViewData:
      public interface ISiteMasterViewData
       
    2. Specify that the master page should require this type of ViewData:
      public partial class Site : System.Web.Mvc.ViewMasterPage<ISiteMasterViewData>
       
    3. Define a custom ViewData class than implements the MasterPage ViewData interface:
      public class AboutViewData : ISiteMasterViewData
       
    4. Specify my View (which is assigned to use the MasterPage for display) to use the custom ViewData class:
      public partial class About : ViewPage<AboutViewData>
       
    5. From the controller, construct the custom AboutViewData instance and pass it via the RenderView method:
      AboutViewData data = new AboutViewData();
      RenderView("About", data);

    Unfortunately, when I compile and run this setup I get the following exception:

    A ViewMasterPage<TViewData> can only be used with content pages that derive from ViewPage<TViewData>.

    It seems like the MVC Framework wants to force the same explicit type for both the page as well as any masters. I would have thought that the Master page would receive the same ViewData instance that the Page receives during rendering. However, when I trace the Type of the ViewData that the Master page is receiving in the above scenario I only get the generic System.Web.Mvc.ViewData. Is this a bug? Or is there another way to pass a separate, distinct ViewData object to the Master page?

    -Ian

  • Re: MasterPage<TViewData> Problem

    12-17-2007, 10:39 AM
    • Loading...
    • tgmdbm
    • Joined on 12-17-2007, 2:08 PM
    • Posts 815
    • Points 3,797
    • ASPInsiders

    simply define your about page as
     
    public partial class About : ViewPage<ISiteMasterViewData>

    and pass the data in as the interface type

    ISiteMasterViewData data = new AboutViewData();
    RenderView("About", data);

    obviously the names will probably change.

     
    you can also add a method to the About page to cast back to the AboutViewData type.

     
    Its not ideal tho... The master page should be able to do the cast for you.
     

  • Re: MasterPage<TViewData> Problem

    12-17-2007, 11:08 AM

    Yeah, I realize that would be a workaround, although it's a pretty significant hack. I'd rather have intellisense on the ViewData for the About page (and all other pages) be generated from the custom, page-specific ViewData objects, rather than the generic ISiteMasterViewData interface. I think a better approach would be to add any special workaround casting to the Master page itself. I've noticed that, in my above example, the MasterPage.ViewContext.ViewData contains the AboutViewData object while the MasterPage.ViewData property contains only the default MVC.ViewData object (which is empty). Thus, I can leave things as they are coded and manually cast the ViewContext.ViewData to ISiteMasterViewData within the master page code. Not sure why the MVC framework isn't doing this itself... but it works as a workaround until I can get an answer as to whether or not this behavior is by design.

    -Ian

  • Re: MasterPage<TViewData> Problem

    12-18-2007, 5:09 PM
    Answer
    • Loading...
    • abombss
    • Joined on 06-27-2006, 4:13 PM
    • Chicago, IL
    • Posts 164
    • Points 574

     For sure a bug.  Here is a fix if microsoft decides to apply it.

    Change the ViewData property of ViewMasterPage<TViewData> from:

     

    public TViewData ViewData
    {
    get
    {
    ViewPage page = this.Page as ViewPage<TViewData>;
    if (page == null)
    {
    throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, AtlasWeb.ViewMasterPage_RequiresViewPageTViewData, new object[0]));
    }
    return page.ViewData;
    }
    }

    To

    public TViewData ViewData
    {
    get
    {
    TViewData viewData = ((ViewPage)Page).ViewData as TViewData;
    if (viewData == null)
    {
    throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, AtlasWeb.ViewMasterPage_RequiresViewPageTViewData, new object[0]));
    }
    return viewData;
    }
    }

     You think MS will give me recongnition points for this?
     

    Adam Tybor -- abombss.com
    Filed under: ,
  • Re: MasterPage<TViewData> Problem

    12-21-2007, 7:15 AM
    Answer
    • Loading...
    • rjcox
    • Joined on 12-19-2007, 2:14 PM
    • Basingstoke, UK
    • Posts 1,444
    • Points 7,054

    abombss:

     You think MS will give me recongnition points for this?

     

    Maybe... but the fix does not work (unfortunetly), because ViewPage.ViewData is of type System.Web.Mvc.ViewData, which is a wrapper around the underlying _viewData field and its type has no relation to the static type parameter of MasterViewPage<T>.

    Cast the page to an IViewDataContainer to access the underlying object, which should then be able to be cast to the required strong ViewData type.

    However a workaournd to this bug that allows use of ViewMasterPage<TSuperTypeOfPageViewDataType> should be possbile.

    Create a class "MyViewMasterPage<T>" derived from ViewMasterPage<T>, with a single member

     

    public new TViewData ViewData {
    get {
    IViewDataContainer page = this.Page;
    TViewData d = page.ViewData as TViewData;
    if (null == d) {
    throw new InvalidOperationException(...)
    }
    return d;
    }
    }

    Richard
  • Re: MasterPage<TViewData> Problem

    12-21-2007, 7:21 AM
    • Loading...
    • abombss
    • Joined on 06-27-2006, 4:13 PM
    • Chicago, IL
    • Posts 164
    • Points 574

     Nice catch, thats a great interim solution.

    Adam Tybor -- abombss.com
  • Re: MasterPage<TViewData> Problem

    01-18-2008, 4:33 PM
    Answer

    I had trouble with rjcox's solution, but I found modifying his included code to below, worked for me.

     

    public class MasterPageBugFix : System.Web.Mvc.ViewMasterPage
    {
        public new TViewData ViewData
        {
            get
            {
                TViewData d = (TViewData)this.ViewContext.ViewData;
                if (null == d)
                {
                    throw new InvalidOperationException();
                }
                return d;
            }
        }
    }
Page 1 of 1 (7 items)