在基页类中使用用户控件

时间:2009-05-21 18:54:08

标签: c# .net asp.net vb.net

我有一个继承自Page的基页类。我们称之为SpiffyPage。

我还有一个名为SpiffyControl的用户控件。

然后我有一个aspx页面,ViewSpiffyStuff.aspx,它继承自SpiffyPage。

在页面加载时,SpiffyPage类应该将SpiffyControl注入到aspx页面。

问题是SpiffyControl是一个用户控件,并且实例化后面的代码必须访问ASP命名空间,有点像:

ASP.controls_spiffycontrol_aspx MyControl;

但SpiffyPage不是aspx页面,它只是一个基页,因此我无法访问ASP命名空间,因此我无法实例化SpiffyControl以便注入它。

我如何实现目标?

编辑:

重要的一点是我必须引用实际控件类型,以便为某些自定义属性赋值。这就是为什么像LoadControl这样返回类型Control的函数在这种情况下不起作用。

11 个答案:

答案 0 :(得分:10)

为您的SpiffyControl添加一个基类,如:

public class SpiffyBase : UserControl
{
    public void DoIt()
    {
        Response.Write("DoIt");
    }
}

然后你让你的SpiffyControl继承那个基类,比如:

public partial class SpiffyControl : SpiffyBase

那么你应该能够从一个Basepage类中做到这一点:

public class BasePage : Page
{
    protected override void OnLoad(EventArgs e)
    {
        var c = Page.LoadControl("SpiffyControl.ascx") as SpiffyBase;

        Page.Controls.Add(c);

        c.DoIt();
    }
}

答案 1 :(得分:4)

我将在单独的ASP.NET服务器控件项目中实现SpiffyControl,并从Web应用程序引用该项目。这样,控件不会驻留在ASP命名空间中,而是存在于您选择的命名空间中,并且行为与任何其他服务器控件一样。

更新:将用户控件放入单独的项目中的一个很好的副作用是它们与Web应用程序本身变得更加分离,这反过来又使它们更加可重用,并且根据它们的设计,它们也更加可测试

答案 2 :(得分:2)

你们是不是在开玩笑吧?我知道ASP.NET webforms是一种痛苦的经历,但没有人听说过ClassName属性,即

<%@ Control ClassName="SpiffyControl" Language="C#" AutoEventWireup="true" CodeFile="SpiffyControl.ascx.cs" Inherits="Controls_SpiffyControl"  %>

因此,原始的例子:

SpiffyControl spiffy = (SpiffyControl)LoadControl("~spiffy.ascx");

答案 3 :(得分:1)

我不确定这是否会对您有所帮助,但有一个名为LoadControl的方法可以使用usercontrol文件名或usercontrol的类型动态加载用户控件。该方法位于System.Web.UI.TemplateControl类中。

Control userControl= LoadControl("usercontrol.ascx");
panelControl.Controls.Add(userControl);

答案 4 :(得分:1)

您可以将SpiffyControl移动到库中 - 这将为其提供更精确的命名空间,并允许您在其他项目中使用它。但这有点复杂 - 你已经制作了UserControl,但它不能在库中工作。 UserControls使用代码隐藏模型 - 它们由两个文件组成,一个标记和一个代码。库似乎不支持代码隐藏模型,因此您无法使用UserControl。您需要将该用户控件转换为Web控件。

创建一个新的类库项目。打开项目的属性,并将AssemblyName和Default Namespace设置为SpiffyLibrary。

现在我们需要转换您的UserControl。首先,在新库中创建一个类。称它为SpiffyControl,就像你现有的用户控件一样,但是让它继承自System.Web.UI.WebControls.CompositeControl。

其次,打开现有用户控件的代码并复制类中的所有内容。然后返回到新类并将其全部粘贴到内部。如果您正在使用任何标准控件事件,则可能需要重命名这些函数以覆盖相应的事件。例如,

protected void Page_Load(object sender, EventArgs e)
{
    ...
}

......应该成为......

protected override void OnLoad(EventArgs e)
{
    ...

    base.OnLoad(e);
}

第三个(这是复杂的部分)您需要重现SpiffyControl标记文件中的所有标记。您不能只复制它 - 而是需要覆盖从CompositeControl继承的CreateChildControls()函数。标记中的每个标记都需要像变量一样进行实例化,并添加到类的Controls集合中。例如,如果您现有的标记文件如下所示:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="SpiffyControl.ascx.cs" Inherits="Controls_SpiffyControl" %>

<div id="Div1" runat="server">
    <asp:Label ID="TextOutput" runat="server" />
</div>

...然后你的新CreateChildControls函数应如下所示:

HtmlGenericControl Div1;
Label TextLabel;

protected override void CreateChildControls()
{
    Div1 = new HtmlGenericControl("div");
    this.Controls.Add(Div1);

    TextLabel = new Label();
    Div1.Controls.Add(TextLabel);

    base.CreateChildControls();
}

请注意,控件声明为类字段;这样,它们对所有类方法都可见。

转换标记后,编译库项目并将其添加到网站项目的引用中。然后打开您网站的web.config文件并找到system.web / pages / controls部分。此部分应该已经有这样的标记:

<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

该标记允许您使用ASP核心库中的控件。所以我们要为我们的库添加一个就像它一样:

<add tagPrefix="spiffy" namespace="SpiffyLibrary" assembly="SpiffyLibrary" />

现在,您网站中的任何地方都可以输入这样的标记:

<spiffy:SpiffyControl ID="SpiffyInstance" runat="server" />

...这将从您的自定义库中加载您的Web控件。

答案 5 :(得分:0)

即使您的基础不是ASPX页面,您也可以从同一框架类继承它,就好像它是:

public abstract class MyBasePage : System.Web.UI.Page
{

}

答案 6 :(得分:0)

假设SpiffyControl和SpiffyPage在同一名称空间中:

 Control spiffyControl = LoadControl("SpiffyControl.ascx");
 (control as SpiffyControl).SpiffyControlProp1 = true;
 (control as SpiffyControl).SpiffyControlProp2 = "Hello, World!";

答案 7 :(得分:0)

您也可以手动定义SpiffyControl的命名空间。为此,您需要更改代码隐藏和标记。

在代码隐藏中,将您的类包装在命名空间块中。所以,如果你的班级是

public partial class Controls_SpiffyControl : System.Web.UI.UserControl
{
    ...
}

...然后将其更改为......

namespace Spiffy
{
    public partial class Controls_SpiffyControl : System.Web.UI.UserControl
    {
        ...
    }
}

然后在标记中调整@Control标头。所以如果你的@Control标题是

<%@ Control
    Language="C#"
    AutoEventWireup="true"
    CodeFile="SpiffyControl.ascx.cs"
    Inherits="Controls_SpiffyControl" %>

...然后将其更改为......

<%@ Control
    Language="C#"
    AutoEventWireup="true"
    CodeFile="SpiffyControl.ascx.cs"
    Inherits="Spiffy.SpiffyControl" %>

答案 8 :(得分:0)

假设您将在支持反射的环境中运行,您可以使用这些方法。

private void SetValue(Control control, string propertyName, object value)
{
    Type type = control.GetType();
    PropertyInfo property = type.GetProperty(propertyName);
    property.SetValue(control, value, null);
}
private object GetValue(Control control, string propertyName)
{
    Type type = control.GetType();
    PropertyInfo property = type.GetProperty(propertyName);
    return property.GetValue(control, null);
}

并称之为:

     Control control = this.LoadControl("~/SpiffyControl.ascx");
    string propertyName = "Custom1";
    object value = "Value";

    SetValue(control, propertyName, value);

如果它被调用很多,你可能想要缓存类型信息。

您还可以考虑在SpiffyPage所在的程序集中创建一个ISpiffyControl接口,然后让您的用户控件实现它。

答案 9 :(得分:0)

已被触及,但界面应该有效。使用任何成员/方法创建ISpiffyControl,在SpiffyControl.ascx.vb中实现它并在SpiffyPage中执行此操作:

Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    MyBase.OnLoad(e)
    Dim page As New Page
    Dim path As String = request.ApplicationPath
    Dim control as UI.Control = page.LoadControl(String.Format _
        ("{0}/pathToUserControl/{1}.ascx", path, controlName))
    Dim sc As ISpiffyControl = CType(control, ISpiffyControl)
    sc.DoSomethingSpecial()
    Me.Controls.Add(sc)
End Sub

答案 10 :(得分:0)

如果我正确阅读,原始海报就会遇到这样的问题:无法访问其UserControl中父页面派生的基类类型,而不是相反。碰巧我遇到了同样的问题。

在我的情况下,我项目中的所有页面都从相同的基础继承,所以我并不担心我的用户控件知道页面的基类。向建筑师道歉。

但是,我通过以下方法解决了这个问题:1)使用基类进行用户控制,2)使用公共名称空间。这启用了我需要的可见性,因为由于某种原因,用户控件的基类仍然无法看到页面基类的基类类型。

无论如何,我只是想我会分享我的结果,以防他们将来帮助别人。

HTH。