代码之家  ›  专栏  ›  技术社区  ›  3Dave

ASP.NET MVC:很好地替代了用户控件?

  •  11
  • 3Dave  · 技术社区  · 15 年前

    我发现用户控件在使用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注册控件,就像在Web窗体中一样:

    <%@ 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测试的。

    自定义标记引用了从testcontrol类继承的ascx局部视图。然后控件重写 Render() 方法,它被调用来呈现视图,使您能够完全控制从标记到输出的过程。

    使用此方法与调用之间的区别 Html.RenderPartial() 或者“html.renderAction()”将控件添加到视图中是通过一个WebForms(如标记)来完成的,这不仅对设计人员更为舒适,而且使他们不必知道控制器名称和方法。控件类的名称与ascx是独立的,这也使得将它们放到程序集中并在不同的项目中重用更容易。

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

    5 回复  |  直到 15 年前
        1
  •  4
  •   Community CDub    8 年前

    乍一看,MVC不具备可重用组件的功能,很容易被忽略。

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

    我认为您所做的只是查看MVC的视图方面,而不是如何将所有底层M和C封装并绑定在一起。部分视图、渲染操作/部分只是MVC底层组件功能的一小部分。封面下面的内容丰富得多。

        2
  •  5
  •   Justin Niessner    15 年前

    我有点困惑。

    首先,相当于用户控件的.NET MVC是 Partial Views . 局部视图是在单个位置封装公共视图功能的一种方便方法。然后,可以从另一个视图内部调用局部视图。

    其次,修改视图并不意味着还需要修改控制器。如果仅仅因为视图发生了更改(而不是基础数据),就需要对两者都进行更改,那么在这一行的某个地方就会出现代码问题。

        3
  •  2
  •   Omu    15 年前

    用户控制 只是一些 呈现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);
    
        4
  •  1
  •   3Dave    15 年前

    请考虑以下示例:

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

      接口ICustomerDetail {
      字符串名称get;
      地址当前地址获取;
      }

    • 我可以创建一个绑定到IAddress视图模型的分部视图地址.ascx

    • 创建customerdetail.ascx时,可以将address.ascx放在同一个表面上,并将其绑定到 oCustomerDetail.Address 领域

    • IMO-我们应该在MVC中从多个这样小的局部视图组合视图,这是您将看到可重用性和用户控件(局部视图)的强大功能的地方。

    • 现在,如果我的控制器返回icustomerdetail,我将能够重新使用address.ascx而不会出现任何问题。

    Hth.

        5
  •  1
  •   Neil T.    15 年前

    以电子商务网站的注册页为例。您提示用户输入他们的姓名、密码、邮政信息、最喜欢的狗品种等。在应用程序的其他地方,您还需要收集账单地址和送货地址。要强制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 截面 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); %>
    

    在送货地址页中:

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

    我可以直接从账单和发货页传递模型,因为类直接从地址下降。只要有正确处理地址的逻辑,就不必对控制器做很多更改(如果有的话)。