【发布时间】:2010-05-10 16:55:43
【问题描述】:
我发现用户控件在使用 ASP.NET 网络表单时非常有用。通过使用标记封装显示控件所需的代码,可重用组件的创建非常简单且非常有用。
虽然 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,但我相信这种方法在功能上等同于将部分视图和控制器捆绑在一起,同时保持清晰的标记。然而,应该清楚的是,在控制中只保留与表示相关的逻辑仍然取决于开发人员。业务和数据访问逻辑仍然属于各自的层。
【问题讨论】:
-
Argh...由于您更改了问题,您的编辑令人沮丧。 MVC 的“可重用、打包控件”与 RenderPartial 和 RenderAction 生活在一个完全不同的世界中。 -- 你开始谈论的是 RenderPartial 与 RenderAction 的辩论/讨论,之前已经回答过,主要是关于代码责任的哲学论证。
-
@jfar ??我添加了“选项”部分,因为这些似乎是每个人都提供的解决方案。如果你有更好的主意,我会很高兴听到它。
-
@David Lively - 如果不定义您尝试解决的解决方案的范围,您将无法比较选项 RenderPartial 和 Portable Areas。这就像说“我应该如何杀死这个虫子?苍蝇拍、棒球棒或轨道上的核武器”,而没有告诉对方你是想用酸性血液杀死蚊子还是合成的外来物种。当您说“可重用的打包控件”时,您谈论的是一个巨大的问题,但是通过将 RenderPartial 作为选项包括在内,您会说“简单的标记重用”。哪一个?你想杀死什么样的虫子?
-
@jfar 好点。我需要能够在整个站点中包含类似于 webforms 用户控件的组件,但不是针对整个站点,也不是在属于母版页的级别。这些组件应该有自己的代码不仅仅是标记(与业务层交互),如果页面控制器不需要了解控件,那就太好了。由于 MVC 用户控件没有代码隐藏,因此我看不到这样做的好方法。清楚吗?
-
@David Lively 我希望我能这样说而不会听起来刻薄,但您不认为这些细节可能存在于您的原始问题中吗?基于这些细节,答案很明确:“使用 RenderAction”