依赖注入:在aspx页面中注入用户控件

时间:2009-11-20 06:21:31

标签: asp.net dependency-injection

我需要构建一个'可自定义的'asp.net web应用程序(不是asp.net mvc)。

我正在考虑使用IoC容器将用户控件注入aspx页面。

有人试过吗?

简而言之,我认为这是如何实现的:

  • 我使用Scott Guthrie的方法构建可重用的“用户控件库”(请参阅​​他的文章“创建和使用用户控件库”)。

  • 该库中的控件和页面应具有动态接收用户控件的位置(例如,在asp PlaceHolder中)。

  • 在我的自定义Web应用程序中,我创建了特定的用户控件。

  • 我将它们以某种方式放在IoC容器中,以便它们可以注入“用户控件库”的控件/页面中(例如,在PlaceHolders中)。

就是这样。

这基本上可以通过这里讨论的http://blogger.forgottenskies.com/?p=70来解决Spring.Net,但是要注入用户控件。

有没有人对这些东西有过一些经验?还是听起来很傻?替代方案?

我们的想法是可以在我的“用户控件库”之上构建不同的Web应用程序,而无需触及该库。

例如:在我的库中,我有一个包含4个文本框的页面,对于一个特定的应用程序,我需要添加一个文本框,而不更改页面的代码。我们的想法是在该页面中放置一个占位符,并在此占位符中动态注入我的自定义文本框。

2 个答案:

答案 0 :(得分:4)

它与您的情况不完全相同,但我需要根据正在编辑的实体的运行时类型动态注入编辑器控件。我让所有的控件继承自这样的通用界面:

public interface IEditor
{
    bool CanHandle(EntityBase entity);
    void Display(EntityBase entity);
    void Save(EntityBase entity);
}

public partial class AddressEditor : UserControl, IEditor
{
    public bool CanHandle(EntityBase entity)
    { 
        return (entity is Address);
    }

    public void Display(EntityBase entity)
    {
        var address = (Address)entity;
        addressLine1Textbox.Text = address.Line1;
        // etc...
    }

    public void Save(EntityBase entity)
    {
        var address = (Address)entity;
        address.Line1 = addressLine1Textbox.Text;
        // etc...
    }
}

然后使用IoC容器(在本例中为StructureMap)我可以使用

之类的东西获得正确的用户控件
var editorControl = ObjectFactory.GetAllInstancesOf<IEditor>().First(x => x.CanHandle(myEntity));

这就是它变得棘手的地方,如果您像这样创建一个实例,则无法向页面添加用户控件。您需要知道它的.ascx文件的位置。您可以通过编辑器界面公开它,以便每个控件都返回它:

public string AscxFile { get { return "~/UserControls/AddressEditor.ascx"; } } // Implements IEditor.AscxFile

然后在调用页面中,使用LoadControl:

var actualControl = LoadControl(editorControl.AscxFile);
editorPlaceholder.Controls.Add(actualControl);
editorControl.Display(myEntity);

我更喜欢使用复合控件,他们需要花费更多精力来创建,但无需担心.ascx文件,我可以直接将其添加到页面中:

var editorControl = ObjectFactory.GetAllInstancesOf<IEditor>().First(x => x.CanHandle(myEntity));
editorPlaceholder.Controls.Add((Control)editorControl);
editorControl.Display(myEntity);

确保你在Page.OnInit阶段添加控件,以便它可以参与ViewState,并且不会在回发之间记住它,所以你必须每次都这样做。

最后一步是使用您选择的IoC工具进行连接,我使用StructureMap自动加载IEditor的所有实例,但我可以在应用程序代码或xml配置中的任何位置改变它。< / p>

答案 1 :(得分:1)

我们使用类似的模型构建了一套应用程序 我们有一个所谓的PageBuilder,它通过注入UserControls和WebParts构建每个页面。

一切都是可配置的(页面布局,页面控件,控件位置等),因此无需更改页面,UserControls或WebParts中的任何代码。 (除非需要添加/更改某些功能)。

我们甚至在控件中有配置和设置来根据SQL查询,模式,当前页面等各种方式更改行为。

基本上它的工作原理如下:

  • 为当前页面创建布局表。
  • 获取要显示的UserControls和WebParts。
  • 将每个UserControl和WebPart放在正确的位置。
  • 将设置应用于每个UserControl和WebPart。

所有WebParts都可以通过自定义发布者/订阅者事件模型相互通信。即在WebPart A中,下拉选择被更改=&gt; WebPart B显示所选项目的数据。

该模型允许我们构建高度可配置的应用程序,客户可以在这些应用程序中设计布局和行为,而无需我们参与。

您的模型似乎是我们模型的一个子集,我只能说它很容易使用。从开发人员的角度和客户两方面来看。

修改
基本上,我们的框架包含一些调用PageBuilder的母版页和基页 每个母版页用于不同类型的对象:页面,用户控件,WebPart,灯箱等 每个aspx页面都包含一个PlaceHolder,用于UserControls和WebParts。这个PlaceHolder由PageBuilder填充。

我们的aspx页面中的UserControl / WebPart PlaceHolder可以填充我们想要的任何控件。因此根本不需要更改aspx页面。如果我们想要一个文本框,我们可以配置它。自定义UserControl或WebPart也是如此。通过这种方式,我们不需要为每个自定义应用程序重新创建aspx页面,但只需要更改配置。

我们为各种aspx页面提供了100多个UserControls和WebParts,但大多数aspx页面看起来类似于:

<%@ Page MasterPageFile="main.master" ... %>
<asp:Content runat="server" ContentPlaceHolderID="Main" ID="MainSection">
  <asp:PlaceHolder runat="server ID="UserControlPlaceHolder"></asp:PlaceHolder>
</asp:Content>

在代码隐藏中我们有类似的东西:

Partial Class MyPageClass Inherits BasePage
  Protected Sub Page_Init(ByVal sender As Object, ByVal e as System.EventArgs) Handles Me.Init
    'The following method is in the BasePage and is part of the PageBuilder.
    LoadControls()
  End Sub
End Class

PageBuilder创建布局,然后加载并将所有控件和WebPart添加到正确的位置 (控制和控制位置的布局都是从配置中获取的。)

然后,PageBuilder为每个Control和WebPart应用设置。这些设置也是可配置的。设置可以是控件高度这样简单的事情,也可以是更复杂的事情,如“显示模式”(静态,页面相关,依赖于组等)。

希望这能更详细地解释它。