ASP.NET MVC:用户控件的良好替代?

时间:2010-05-10 16:55:43

标签: asp.net-mvc user-controls

我发现用户控件在使用ASP.NET webforms时非常有用。通过使用标记封装显示控件所需的代码,可重用组件的创建非常简单,非常非常有用。

虽然MVC提供了方便的关注点分离,但这似乎打破了封装(即,您可以在不添加或使用其支持代码的情况下添加控件,从而导致运行时错误)。每次我向视图添加控件时都必须修改控制器,这似乎是我要集成关注点,而不是将它们分开。我宁愿打破纯粹的MVC意识形态,也不愿放弃可重复使用的打包控件的好处。

我需要能够在整个站点中包含类似于webforms用户控件的组件,但不能包含整个站点,而不是属于母版页的级别。这些组件应该有自己的代码,而不仅仅是标记(与业务层交互),如果页面控制器不需要知道控件,那将是很好的。由于MVC用户控件没有代码隐藏,我看不到这样做的好方法。

更新 最后,这是一种很好的(并且,回想起来,很明显)实现这一目标的方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace K.ObjectModel.Controls
{
    public class TestControl : ViewUserControl
    {
        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            writer.Write("Hello World");
            base.Render(writer);
        }
    }
}

创建一个继承ViewUserControl

的新类

覆盖.Render()方法,如上所示。

通过其关联的ASCX注册控件,就像在webForm中一样:

<%@ Register TagName="tn" TagPrefix="k" Src="~/Views/Navigation/LeftBar.ascx"%>

在您需要的任何视图或母版页中使用相应的标记:

<k:tn runat="server"/>

确保您的.ascx继承了您的新控件:

<%@ Control Language="C#" Inherits="K.ObjectModel.Controls.TestControl" %>

瞧,瞧,你已经开始运转了。这是使用ASP.NET MVC 2,VS 2010和.NET 4.0进行测试的。

您的自定义标记引用ascx部分视图,该视图继承自TestControl类。然后控件覆盖Render()方法,该方法被调用以呈现视图,使您可以完全控制从标记到输出的过程。

使用这种方法和调用Html.RenderPartial()或`Html.RenderAction()'之间的区别是将控件添加到视图中,使用类似webforms的标记,这不仅对设计人员来说更舒服,而且使他们不必知道控制器名称和方法。控件类的名称与ASCX隔离,也可以更容易地将它们放入程序集中,并在不同的项目中重用它们。

有人可能会说这违反了SoC,但我相信这种方法在功能上等同于将部分视图和控制器绑在一起,同时保持干净的标记。但是,应该很清楚,开发人员仍然只能在控件中保留与表示相关的逻辑。业务和数据访问逻辑仍属于各自的层。

5 个答案:

答案 0 :(得分:5)

我在这里有点困惑。

首先,与用户控件等效的.NET MVC是Partial Views。部分视图是将常见视图功能封装在单个位置的便捷方式。然后,您可以从另一个视图中调用部分视图。

其次,修改View并不意味着修改控制器。如果您需要仅因为View更改(而不是基础数据)而对两者进行更改,那么就会出现代码问题。

答案 1 :(得分:4)

乍一看,很容易将MVC视为没有可重用组件的功能。

一旦你了解了ASP.NET MVC,你会发现有several techniques for creating rich controls and components并且封装了MVC的方面跟随along the same pathways 封装WebForms应用程序。

我认为你所做的只是查看MVC的View方面,而不是如何将所有底层M和C封装在一起。部分视图,渲染动作/部分只是MVC底层组件功能的一小部分。在幕后有更多的丰富内容。

答案 2 :(得分:2)

用户控件只是一些呈现html 的内容,在mvc中你有html帮助器和部分视图以及普通视图(你可以使用renderaction渲染它们)

Html.Helper("someStuff")
Html.RenderPartial("viewname")
Html.RenderAction<Controller>(o => o.Action());

所以基本上它只是助手

您实际上可以轻松地将来电替换为

Html.TextBoxFor(o => o.Name);

Html.RenderPartial("textbox", Model.Name);

答案 3 :(得分:1)

考虑以下示例:

  • 我的视图(CustomerDetail.ascx)绑定到ICustomerDetail视图模型,如下所示:

    interface ICustomerDetail {
        string Name {get; }
        地址CurrentAddress {get; }
    }

  • 我可以创建一个部分视图Address.ascx,它绑定到IAddress视图模型

  • 当我创建CustomerDetail.ascx时,我可以将Address.ascx放在同一表面上。将其绑定到oCustomerDetail.Address字段

  • IMO - 我们应该在MVC&amp; amp;中创建来自多个这样较小的部分视图的视图。这是您将看到可重用性和可用性的地方。用户控制的力量(部分视图)

  • 现在,如果我的控制器返回ICustomerDetail,我将能够毫无问题地重复使用Address.ascx

HTH。

答案 4 :(得分:1)

我们以电子商务网站的注册页面为例。您提示用户输入他们的姓名,密码,邮政信息,喜欢的狗品种等。在应用程序的其他地方,您还需要收集帐单邮寄地址和送货地址。要强制执行DRY,您需要创建一个用户控件来管理地址信息的输入。

因此,为了进一步说明,您的地址类看起来像这样:

public class Address
{
    public string StreetAddress { get; set; }
    public string City { get; set; }
    ...
}

您的注册类:

public class UserReg
{
    public string UserName { get; set; }
    public Address MailingAddress { get; set; }
    ...
}

您的结算地址和送货地址可能来自地址类:

public class BillingAddress : Address
{ 
    ...
}

public class ShippingAddress : Address
{ 
    ...
}

对于以下示例,我假设您已将System.Web.Mvc添加到namespaces的{​​{1}}部分。基于此类层次结构,您的用户控件将具有仅引用Address类的控件标记:

web.config

由于您已完成此操作,因此只需从页面传递相应的模型引用即可。在“用户注册”页面中:

<%@ Control Language="C#" Inherits="ViewUserControl<Address>" %>

在结算地址页面中:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<UserReg>" %>
    ...
    <% Html.RenderPartial("AddressControl", Model.MailingAddress); %>

在送货地址页面中:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<BillingAddress>" %>
    ...
    <% Html.RenderPartial("AddressControl", Model); %>

我可以直接从结算和发货页面传递模型,因为该类直接来自地址。只要逻辑正确处理地址,您就不必对控制器进行很多更改(如果有的话)。