我有一个继承自Page的基页类。我们称之为SpiffyPage。
我还有一个名为SpiffyControl的用户控件。
然后我有一个aspx页面,ViewSpiffyStuff.aspx,它继承自SpiffyPage。
在页面加载时,SpiffyPage类应该将SpiffyControl注入到aspx页面。
问题是SpiffyControl是一个用户控件,并且实例化后面的代码必须访问ASP命名空间,有点像:
ASP.controls_spiffycontrol_aspx MyControl;
但SpiffyPage不是aspx页面,它只是一个基页,因此我无法访问ASP命名空间,因此我无法实例化SpiffyControl以便注入它。
我如何实现目标?
编辑:
重要的一点是我必须引用实际控件类型,以便为某些自定义属性赋值。这就是为什么像LoadControl这样返回类型Control的函数在这种情况下不起作用。
答案 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。