繁体   English   中英

在基页类中使用用户控件

[英]Using a user control in a base page class

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

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

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

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

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

ASP.controls_spiffycontrol_aspx MyControl;

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

我怎样才能实现目标?

编辑:

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

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

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

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

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

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

因此,原始的例子:

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

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

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

您可以将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控件。

如果我正确地读到这个,那么原始海报就会遇到这样的问题,即他无法访问父网页在其UserControl中派生的基类的类型 ,而不是相反。 碰巧我遇到了同样的问题。

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

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

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

HTH。

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

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

}

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

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

您也可以手动定义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" %>

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

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

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

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

它已被触及,但界面应该有效。 使用任何成员/方法创建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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM