【问题标题】:Supporting ViewState in an MVC ViewUserControl在 MVC ViewUserControl 中支持 ViewState
【发布时间】:2011-09-15 18:35:44
【问题描述】:

我知道我知道,这不合常理。在大家想讲MVC模式之前,这是一个极端的措施。我有一个 ASP.NET 控件,我必须在需要 ViewState 的 MVC 中工作,并且希望能够专注于这一点。我知道我可以load a classic ASP.NET pages in MVC by a trick of routes,但我需要一个适合 MVC 框架的整体解决方案。

必须有一种方法来扩展 ViewUserControl 以与 ViewState 游戏一起玩,或者通过过滤器或属性,甚至通过解析请求并以某种方式覆盖管道早期的某些内容以调用 LoadViewState 来模拟 ViewState 对象。

唉,恐怕我对经典的 ASP.NET 不够精通,不知道如何做到这一点。 ViewUserControl 具有派生自 UserControl 对象的 ViewState 属性以及 SaveViewState 和 LoadViewState 方法,因此我看到了这样做的一些希望。

这是我目前所拥有的:

<%@ 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>

局部视图背后的代码:

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();
        }
    }
}

有趣的是,SaveViewState 被调用,但 LoadViewState 从来没有。

【问题讨论】:

  • 所以我假设既然您可以在 MVC 项目中拥有一个 Web 表单页面,那么您就不能为这个控件恢复到 Web 表单方法吗?另外,这是一个 ASP.NET 自定义控件,它是您自己的还是第三方供应商?
  • 嘿@Brian,是的,不。对这个控件使用经典的 asp.net 模式(如此处所述:weblogs.asp.net/rajbk/archive/2010/05/11/…)是我最后的手段。问题是它是具有安全属性和模型吊索的广泛 MVC 应用程序的一部分。为了使 asp.net 页面与安全性和传入模型相提并论,这将导致大量的改造。尽管我们可以扩展 MVC,但我拒绝相信从填充的请求中破解 ViewState 并让 MVC 与它一起使用是不可能的。
  • 这是一个三十方控制,Telerik 的 RadGrid。我可以访问源代码,并按照一系列步骤一直回到简单的实现,即永远不会调用控件的 LoadViewState。

标签: c# asp.net-mvc asp.net-mvc-3


【解决方案1】:

您正试图将方形钉插入圆孔,但是....

要让 ViewState 工作于整个组合页面(内容 + 主控 + 控件),您需要有一个 &lt;form runat="server"&gt; 包装所有需要 ViewState 的元素。而在 WebForms 中,您只能在一个页面中拥有其中一个。您的控件应位于具有该表单元素的同一页面中。如果你把它放在使用 RenderPartial 等调用的东西中,那么它可能不起作用。如果您将它放在正确的位置,您可能必须确保调用额外的页面事件以保持/加载视图状态。甚至一旦你开始工作,确保控件的服务器端点击事件正常工作可能会更加困难。

我知道你不想听到它,但从长远来看,使用 WebForms 页面或重写东西以使用为 Mvc 设计的组件会更好。

【讨论】:

  • 大声笑,这更像是试图将大象放入圆孔中。这很痛苦!谢谢你的信息顺便说一句。我已经在表单 runat 服务器中使用 ViewState 隐藏参数呈现控件。由于在 MVC 生命周期期间未加载 ViewState,因此在回发(使用 MVC 回发这个词很有趣)时发生了恶臭。您如何建议调用 ViewState 的持久性?
【解决方案2】:

Telerik 在 MVC 框架中支持他们的 RadControls;我用了其中的几个。你可以在这里找到一篇博文:http://blogs.telerik.com/aspnetmvcteam/posts/08-11-06/asp_net_ajax_controls_in_asp_net_mvc.aspx

自从他们发布了开源的 MVC 框架以来,我最近没有这样做。然而,他们已经发布了一组帮助扩展来在 MVC 中使用他们的控件,这些应该在某个地方可用。

所以这应该不是问题。他们还使用 MVC 中的 ASP.NET AJAX 控件进行了示例演示,可在此处获得:http://www.telerikwatch.com/2009/01/telerik-mvc-demo-app-now-available.html

HTH。

【讨论】:

    【解决方案3】:

    所有进入这里的人,不要放弃希望。我能够找到解决方案,虽然它不是很漂亮。它使用一点反射和硬编码映射到对象树。希望这对于任何需要 MVC 中的 ViewState 的人来说都是一个很好的起点。

    局部视图:

    <%@ 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>
    

    后面的代码:

    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;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-01
      • 1970-01-01
      • 2011-11-17
      相关资源
      最近更新 更多