ASP。NET MVC:很好的替代用户控件?

时间:2022-06-19 16:48:16

I found user controls to be incredibly useful when working with ASP.NET webforms. By encapsulating the code required for displaying a control with the markup, creation of reusable components was very straightforward and very, very useful.

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

While MVC provides convenient separation of concerns, this seems to break encapsulation (ie, you can add a control without adding or using its supporting code, leading to runtime errors). Having to modify a controller every time I add a control to a view seems to me to integrate concerns, not separate them. I'd rather break the purist MVC ideology than give up the benefits of reusable, packaged controls.

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

I need to be able to include components similar to webforms user controls throughout a site, but not for the entire site, and not at a level that belongs in a master page. These components should have their own code not just markup (to interact with the business layer), and it would be great if the page controller didn't need to know about the control. Since MVC user controls don't have codebehind, I can't see a good way to do this.

我需要能够在整个站点中包含与webforms用户控件类似的组件,但不能用于整个站点,也不能包含在属于母版页面的级别上。这些组件应该有自己的代码,而不仅仅是标记(以便与业务层交互),而且如果页面控制器不需要知道控件,那就更好了。因为MVC用户控件没有代码隐藏,所以我找不到一个好的方法。

Update FINALLY, a good (and, in retrospect, obvious) way to accomplish this.

最后更新一下,这是一种很好的方法(回顾一下,很明显)。

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

Create a new class which inherits ViewUserControl

创建一个继承ViewUserControl的新类

Override the .Render() method as shown above.

重写. render()方法,如上面所示。

Register the control via its associated ASCX as you would in a webForm:

通过相关的寄存器控制ASCX webForm一样:

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

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

Use the corresponding tag in whatever view or master page that you need:

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

<k:tn runat="server"/>

< k:tn runat = " server " / >

Make sure your .ascx inherits your new control:

确保你的。ascx继承了你的新控件:

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

< % @控制语言= " c# "继承= " K.ObjectModel.Controls。TestControl " % >

Voila, you're up and running. This is tested with ASP.NET MVC 2, VS 2010 and .NET 4.0.

瞧,你起来跑了。这是用ASP测试的。NET MVC 2, VS 2010和。NET 4.0。

Your custom tag references the ascx partial view, which inherits from the TestControl class. The control then overrides the Render() method, which is called to render the view, giving you complete control over the process from tag to output.

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

The difference between using this approach and calling Html.RenderPartial() or `Html.RenderAction()' is adding the control to a view is done with a webforms-like tag, which is not only more comfortable for designers, but keeps them from having to be aware of controller names and methods. The name of the control class is isolated to the ASCX, also making it easier to drop these in an assembly and reuse them across separate projects.

使用这种方法与调用Html.RenderPartial()或' Html.RenderAction()'之间的区别是,将控件添加到视图中是使用类似web表单的标记完成的,这不仅对设计人员更方便,而且使他们不必知道控制器名称和方法。控件类的名称被隔离到ASCX中,这也使得在程序集中放置它们并在不同的项目中重用它们变得更容易。

Some may say that this violates SoC, but I believe that this approach is functionally equivalent to tying a partial view and a controller together while maintaining clean markup. It should be clear, however, that it is still up to the developer to keep only presentation-related logic in the control Business and data access logic still belong in their respective layers.

有人可能会说这违反了SoC,但我认为这种方法在功能上等同于在维护干净标记的同时将部分视图和控制器捆绑在一起。但是,应该清楚的是,在控制业务和数据访问逻辑中,仅保留与显示相关的逻辑仍然属于它们各自的层。

5 个解决方案

#1


4  

At first glance its easy to dismiss MVC as not having the capabilities for reusable components.

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

Once you get the know ASP.NET MVC you'll find there are several techniques for creating rich controls and components and encapsulating aspects of MVC follow along the same pathways as encapsulating a WebForms application.

一旦你知道ASP。您将发现,有几种技术可以创建丰富的控件和组件,以及封装MVC的各个方面,它们遵循与封装WebForms应用程序相同的方法。

I think what your doing is only looking at the View aspects of MVC and not how all the underlying M and C can be encapsulated and tied together. Partial Views, Render Action/Partial are just small pieces of the underlying component capabilities of MVC. There is much more richness under the covers.

我认为您所做的只是查看MVC的视图方面,而不是所有底层的M和C如何被封装和捆绑在一起。部分视图、呈现操作/部分只是MVC底层组件功能的一小部分。书的封面要丰富得多。

#2


5  

I'm a little confused here.

我有点困惑。

First of all, the .NET MVC equivalent to User Controls is Partial Views. Partial Views are a convenient way of encapsulating common View functionality in a single location. You can then call a Partial View from inside another View.

首先,与用户控件等价的。net MVC是部分视图。部分视图是将公共视图功能封装到单个位置的一种简便方法。然后可以从另一个视图内部调用部分视图。

Second of all, modifying a View shouldn't mean also modifying a controller. If you are required to make a change to both just because your View changed (and not the underlying data), then there's a code issue somewhere along the line.

其次,修改视图不应该意味着也修改控制器。如果仅仅因为您的视图(而不是底层数据)发生了更改,就需要对两者进行更改,那么就会出现代码问题。

#3


2  

a user control is just some stuff that renders html, in mvc you have html helpers and partial views and normal views (you can render them with renderaction )

用户控件就是渲染html的东西,在mvc中有html助手,部分视图和普通视图(可以用renderaction渲染)

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

so basically it's just the helpers

基本上就是帮助者

you can actually easily substitute a call to

实际上,您可以轻松地替换一个调用。

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

with

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

#4


1  

Consider the following example:

考虑下面的例子:

  • My view (CustomerDetail.ascx) binds to ICustomerDetail view-model which looks like:

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

    interface ICustomerDetail {
    string Name { get; }
    Address CurrentAddress { get; }
    }

    接口ICustomerDetail {string Name {get;}地址当前地址{get;} }

  • I can create a partial view Address.ascx which binds to IAddress view-model

    我可以创建部分视图地址。与IAddress视图-模型绑定的ascx

  • When I am creating the CustomerDetail.ascx, I can place the Address.ascx on the same surface & bind it to the oCustomerDetail.Address field

    当我创建CustomerDetail时。ascx,我可以放置地址。ascx与oCustomerDetail结合。地址字段

  • IMO - we should be composing views from multiple such smaller partial views in MVC & this is where you will see the re-usability & the power of user controls (partial views)

    我们应该在MVC中从多个这样小的部分视图中组合视图&在这里您将看到可重用性和用户控件的强大功能(部分视图)

  • Now if my controller returns ICustomerDetail, I will be able to re-use the Address.ascx without any problems

    现在,如果我的控制器返回ICustomerDetail,我将能够重新使用这个地址。ascx没有任何问题

HTH.

HTH。

#5


1  

Let's take a registration page for an e-commerce site, as an example. You prompt the user for their name, password, postal information, favorite dog breed, etc. Somewhere else in the application, you also need to collect a billing address and a shipping address. To enforce DRY, you create a user control that manages the entry of the address information.

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

So, to further illustrate, your address class looks something like this:

进一步说明,你的地址类看起来是这样的:

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

Your registration class:

你的注册类:

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

Your billing and shipping addresses may descend from the Address class:

你的帐单及送货地址可能源自地址类别:

public class BillingAddress : Address
{ 
    ...
}

public class ShippingAddress : Address
{ 
    ...
}

For the following examples, I am assuming that you have added System.Web.Mvc to the namespaces section of web.config. Based on this class hierarchy, your user control will have a control tag that refers only to the Address class:

对于以下示例,我假设您已经添加了System.Web。Mvc到web.config的名称空间部分。基于这个类层次结构,您的用户控件将具有仅引用地址类的控制标记:

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

Since you've done this, you simply need to pass the appropriate model reference from the page. In the User Registration page:

既然已经这样做了,您只需从页面中传递适当的模型引用。在用户注册页面:

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

In the billing address page:

在帐单地址页:

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

In the shipping address page:

在运输地址页:

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

I can pass the model directly from the billing and shipping page, because the class directly descends from Address. As long as the logic is in place to process the addresses correctly, you're not going to have to make many changes to the controller, if any.

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

#1


4  

At first glance its easy to dismiss MVC as not having the capabilities for reusable components.

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

Once you get the know ASP.NET MVC you'll find there are several techniques for creating rich controls and components and encapsulating aspects of MVC follow along the same pathways as encapsulating a WebForms application.

一旦你知道ASP。您将发现,有几种技术可以创建丰富的控件和组件,以及封装MVC的各个方面,它们遵循与封装WebForms应用程序相同的方法。

I think what your doing is only looking at the View aspects of MVC and not how all the underlying M and C can be encapsulated and tied together. Partial Views, Render Action/Partial are just small pieces of the underlying component capabilities of MVC. There is much more richness under the covers.

我认为您所做的只是查看MVC的视图方面,而不是所有底层的M和C如何被封装和捆绑在一起。部分视图、呈现操作/部分只是MVC底层组件功能的一小部分。书的封面要丰富得多。

#2


5  

I'm a little confused here.

我有点困惑。

First of all, the .NET MVC equivalent to User Controls is Partial Views. Partial Views are a convenient way of encapsulating common View functionality in a single location. You can then call a Partial View from inside another View.

首先,与用户控件等价的。net MVC是部分视图。部分视图是将公共视图功能封装到单个位置的一种简便方法。然后可以从另一个视图内部调用部分视图。

Second of all, modifying a View shouldn't mean also modifying a controller. If you are required to make a change to both just because your View changed (and not the underlying data), then there's a code issue somewhere along the line.

其次,修改视图不应该意味着也修改控制器。如果仅仅因为您的视图(而不是底层数据)发生了更改,就需要对两者进行更改,那么就会出现代码问题。

#3


2  

a user control is just some stuff that renders html, in mvc you have html helpers and partial views and normal views (you can render them with renderaction )

用户控件就是渲染html的东西,在mvc中有html助手,部分视图和普通视图(可以用renderaction渲染)

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

so basically it's just the helpers

基本上就是帮助者

you can actually easily substitute a call to

实际上,您可以轻松地替换一个调用。

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

with

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

#4


1  

Consider the following example:

考虑下面的例子:

  • My view (CustomerDetail.ascx) binds to ICustomerDetail view-model which looks like:

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

    interface ICustomerDetail {
    string Name { get; }
    Address CurrentAddress { get; }
    }

    接口ICustomerDetail {string Name {get;}地址当前地址{get;} }

  • I can create a partial view Address.ascx which binds to IAddress view-model

    我可以创建部分视图地址。与IAddress视图-模型绑定的ascx

  • When I am creating the CustomerDetail.ascx, I can place the Address.ascx on the same surface & bind it to the oCustomerDetail.Address field

    当我创建CustomerDetail时。ascx,我可以放置地址。ascx与oCustomerDetail结合。地址字段

  • IMO - we should be composing views from multiple such smaller partial views in MVC & this is where you will see the re-usability & the power of user controls (partial views)

    我们应该在MVC中从多个这样小的部分视图中组合视图&在这里您将看到可重用性和用户控件的强大功能(部分视图)

  • Now if my controller returns ICustomerDetail, I will be able to re-use the Address.ascx without any problems

    现在,如果我的控制器返回ICustomerDetail,我将能够重新使用这个地址。ascx没有任何问题

HTH.

HTH。

#5


1  

Let's take a registration page for an e-commerce site, as an example. You prompt the user for their name, password, postal information, favorite dog breed, etc. Somewhere else in the application, you also need to collect a billing address and a shipping address. To enforce DRY, you create a user control that manages the entry of the address information.

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

So, to further illustrate, your address class looks something like this:

进一步说明,你的地址类看起来是这样的:

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

Your registration class:

你的注册类:

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

Your billing and shipping addresses may descend from the Address class:

你的帐单及送货地址可能源自地址类别:

public class BillingAddress : Address
{ 
    ...
}

public class ShippingAddress : Address
{ 
    ...
}

For the following examples, I am assuming that you have added System.Web.Mvc to the namespaces section of web.config. Based on this class hierarchy, your user control will have a control tag that refers only to the Address class:

对于以下示例,我假设您已经添加了System.Web。Mvc到web.config的名称空间部分。基于这个类层次结构,您的用户控件将具有仅引用地址类的控制标记:

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

Since you've done this, you simply need to pass the appropriate model reference from the page. In the User Registration page:

既然已经这样做了,您只需从页面中传递适当的模型引用。在用户注册页面:

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

In the billing address page:

在帐单地址页:

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

In the shipping address page:

在运输地址页:

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

I can pass the model directly from the billing and shipping page, because the class directly descends from Address. As long as the logic is in place to process the addresses correctly, you're not going to have to make many changes to the controller, if any.

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