简体   繁体   中英

Supporting ViewState in an MVC ViewUserControl

I know I know, it's going against the grain. Before everyone wants to lecture about the MVC pattern, this is an extreme measure. I have a ASP.NET control that I must get to work in MVC that requires the ViewState and would appreciate the focus to stay on this. I am aware that I can load a classic ASP.NET pages in MVC by a trick of routes , but I need an integral solution that fits within the MVC framework.

There must be a way to extend a ViewUserControl to play along with the ViewState game, or by a filter or attribute, or even mocking a ViewState object by parsing the Request and somehow overriding something early in the pipeline to invoke the LoadViewState.

Alas, I'm afraid I'm not versed enough with classic ASP.NET to know how to do this. The ViewUserControl has the ViewState property derived from the UserControl object along with SaveViewState and LoadViewState methods, so I see some hope in doing this.

Here is what I have so far:

<%@ Control Language="C#" CodeBehind="RadGridExample.ascx.cs" Inherits="MvcApplication5.Views.Shared.RadGridExample" %>
<form runat="server">

    <telerik:RadScriptManager ID="scriptmanager2" runat="server"></telerik:RadScriptManager>

    <telerik:RadGrid runat="server" ID="RadGrid1" AutoGenerateColumns="false" AllowMultiRowSelection="true" ShowGroupPanel="true">
        <MasterTableView TableLayout="Fixed" >
            <Columns>
                <telerik:GridBoundColumn DataField="Name" HeaderText="Name" DataType="System.String" />
                <telerik:GridBoundColumn DataField="Age" HeaderText="Age" DataType="System.String" />
            </Columns>
        </MasterTableView>
        <ClientSettings AllowDragToGroup="true" >
        </ClientSettings>
    </telerik:RadGrid>

</form>

The code behind for the partial view:

namespace MvcApplication5.Views.Shared
{
    public class RadGridExample : ViewUserControl
    {
        protected void Page_Init()
        {
            HomeIndexViewModel viewModel = this.Model as HomeIndexViewModel;

            RadGrid grid = this.Controls[0].FindControl("RadGrid1") as RadGrid;

            grid.DataSource = viewModel.Animals;

            grid.DataBind();
        }

        protected override void LoadViewState(object savedState)
        {
            base.LoadViewState(savedState);
        }

        protected override object SaveViewState()
        {
            return base.SaveViewState();
        }
    }
}

Funny thing, the SaveViewState gets called, but LoadViewState never does.

You are trying to fit a square peg into a round hole, but....

To get ViewState to work your entire composed page (content + master + controls) requires that you have a <form runat="server"> wrapping all the elements that require ViewState. And in WebForms you can only have one of those in a page. Your control should be in the same page that has that form element. If you put it in something that gets called using RenderPartial etc then it probably won't work. Een if you have it in the right place you might have to ensure that the extra page events get invoked to persist/load the view state. And even once you get that working ensuring that the control's server-side click events work correctly might be even more difficult.

I know you don't want to hear it but in the long run it will be better to use a WebForms page or rewrite things to use a component designed for Mvc.

Telerik supports their RadControls in the MVC framework; I used a few of them. You can find a blog post here: http://blogs.telerik.com/aspnetmvcteam/posts/08-11-06/asp_net_ajax_controls_in_asp_net_mvc.aspx

I haven't done it recently since they released the MVC framework that's open source. However, they had released a set of helper extensions to use their controls in MVC, which should be available somewhere.

So this shouldn't be a problem. They also had a sample demo using the ASP.NET AJAX controls in MVC, available here: http://www.telerikwatch.com/2009/01/telerik-mvc-demo-app-now-available.html

HTH.

All ye who enter here, do not abandon hope. I was able to find a solution to this, though it's not very pretty. It uses a little reflection and hard coded mapping to tree of objects. Hopefully this will be a good starting point for anyone needing ViewState in MVC.

Partial View:

<%@ Control Language="C#" CodeBehind="RadGridExample.ascx.cs" Inherits="MvcApplication5.Views.Shared.RadGridExample" %>
<form runat="server">

    <telerik:RadScriptManager ID="scriptmanager2" runat="server"></telerik:RadScriptManager>

    <telerik:RadGrid runat="server" ID="RadGrid1" AutoGenerateColumns="false" AllowMultiRowSelection="true" ShowGroupPanel="true" AllowFilteringByColumn="True" >
        <MasterTableView TableLayout="Fixed" >
            <Columns>
                <telerik:GridBoundColumn DataField="Name" HeaderText="Name" DataType="System.String" />
                <telerik:GridBoundColumn DataField="Age" HeaderText="Age" DataType="System.String" />
            </Columns>
        </MasterTableView>
        <ClientSettings AllowDragToGroup="true" AllowGroupExpandCollapse="false" >
        </ClientSettings>
    </telerik:RadGrid>

</form>

Code behind:

using System.Reflection;
using System.Web.Mvc;
using System.Web.UI;
using MvcApplication5.Models.ViewModels;
using Telerik.Web.UI;

namespace MvcApplication5.Views.Shared
{
    public class RadGridExample : ViewUserControl
    {
        protected void Page_Init()
        {
            RadGrid grid = this.Controls[0].FindControl("RadGrid1") as RadGrid;

            grid.NeedDataSource += new GridNeedDataSourceEventHandler(grid_NeedDataSource);

            grid.DataBind();

            string viewState = Request.Form["__VIEWSTATE"];

            if (!string.IsNullOrEmpty(viewState))
            {
                LosFormatter formatter = new LosFormatter();

                object savedStateObject = formatter.Deserialize(viewState);

                MethodInfo method = grid.GetType().GetMethod("LoadViewState", BindingFlags.NonPublic | BindingFlags.Instance);

                // TODO: Find a less brittle/more elegant way to isolate the appropiate viewstate object for this control
                // In the case of Telerik's RadGrid, the key wasy find the tree that had an array of 13 objects
                method.Invoke(grid, new object[] { (((((((((savedStateObject as Pair).First as Pair).Second as Pair).Second as System.Collections.ArrayList)[1] as Pair).Second as System.Collections.ArrayList)[1] as Pair).Second as System.Collections.ArrayList)[1] as Pair).First });
            }

            string eventArgument = Request.Form["__EVENTARGUMENT"];

            if (!string.IsNullOrEmpty(eventArgument))
            {
                grid.RaisePostBackEvent(eventArgument);
            }
        }

        void grid_NeedDataSource(object sender, GridNeedDataSourceEventArgs e)
        {
            (sender as RadGrid).DataSource = this.Model as HomeIndexViewModel;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM