我发现用户控件在使用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" %>
您的自定义标记引用ascx部分视图,该视图继承自TestControl类。然后控件覆盖Render()
方法,该方法被调用以呈现视图,使您可以完全控制从标记到输出的过程。
使用这种方法和调用Html.RenderPartial()
或`Html.RenderAction()'之间的区别是将控件添加到视图中,使用类似webforms的标记,这不仅对设计人员来说更舒服,而且使他们不必知道控制器名称和方法。控件类的名称与ASCX隔离,也可以更容易地将它们放入程序集中,并在不同的项目中重用它们。
有人可能会说这违反了SoC,但我相信这种方法在功能上等同于将部分视图和控制器绑在一起,同时保持干净的标记。但是,应该很清楚,开发人员仍然只能在控件中保留与表示相关的逻辑。业务和数据访问逻辑仍属于各自的层。
答案 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); %>
我可以直接从结算和发货页面传递模型,因为该类直接来自地址。只要逻辑正确处理地址,您就不必对控制器进行很多更改(如果有的话)。