2009-05-21 12 views
7

Tengo una clase de página base que hereda de Página. Lo llamaremos SpiffyPage.Uso de un control de usuario en una clase de página base

También tengo un control de usuario llamado SpiffyControl.

Luego tengo una página aspx, ViewSpiffyStuff.aspx, que hereda de SpiffyPage.

En la carga de la página, se supone que la clase SpiffyPage inyecta un SpiffyControl en la página aspx.

El problema es que SpiffyControl es un control de usuario, y para crear una instancia que en el código detrás de uno tiene que acceder al espacio de nombres ASP, algo así como:

ASP.controls_spiffycontrol_aspx MyControl; 

Pero SpiffyPage no es una página aspx, es solo una página base, y como tal no puedo acceder al espacio de nombres ASP, y por lo tanto no puedo instanciar un SpiffyControl para inyectarlo.

¿Cómo puedo lograr mi objetivo?

Editar:

Un punto importante es que debo tener una referencia al tipo de control real para que pueda asignar valores a ciertas propiedades personalizadas. Es por eso que una función como LoadControl, que devuelve el tipo Control, no funcionará en este caso.

+2

Puede usar LoadControl, pero echelo. SpiffyControl spiffy = (SpiffyControl) LoadControl ("usercontrol.ascs"); – Geoff

+0

El problema es que no puedo acceder al tipo SpiffyControl, así que tampoco puedo transmitirlo. Si pudiera lanzarlo, también podría simplemente crear una instancia. –

+0

¿Por qué no puede acceder al tipo SpiffyControl? – Jonas

Respuesta

10

Añadir una clase base a su SpiffyControl, como:

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

y luego hacer su SpiffyControl heredan de que la clase base, como:

public partial class SpiffyControl : SpiffyBase 

, entonces debería ser capaz de hacer esto de una clase 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(); 
    } 
} 
+0

Esta es realmente la solución más limpia y simple para este problema. Una variación es usar una interfaz y luego probar el UserControl después de cargar con: if (control es ISpiffyControl) {((ISpiffyControl)). DoIt(); } – mckamey

+0

Tuve un problema similar y lo resolví de esta manera. La razón de este comportamiento es que el binario de asp.net está dividido en varios dll, y App_Code no ve lo que está en los dll generados por aspx. – devio

4

Implementaría SpiffyControl en un proyecto de control de servidor ASP.NET separado, y haría referencia a ese proyecto desde la aplicación web. De esta forma, el control no residirá en el espacio de nombres ASP, sino en el espacio de nombres de su elección y se comportará como cualquier otro control de servidor.

Actualización: un buen efecto secundario de poner los controles de usuario en un proyecto separado es que se disocian más de la aplicación web como tal, lo que a su vez los hace más reutilizables y, según su diseño, también más comprobable.

+0

¿Estás diciendo que debes poner el Control de usuario en una dll separada? ¿Es eso posible? –

+0

Sure; cuando crea un nuevo proyecto, en la categoría Web puede elegir crear un proyecto de tipo "ASP".NET Server Control ", que le dará un proyecto donde puede implementar uno (o más) controles, y que dará un dll como salida. –

+0

Ah, sí, por supuesto. El problema es que un" control de servidor "no es Lo mismo que "control de usuario". Tengo un proyecto de control de servidor, pero estoy casi seguro de que no puedo crear un control de usuario fuera de un proyecto web. –

1

No estoy seguro si esto lo ayudará pero hay un método llamado LoadControl que puede cargar un control de usuario dinámicamente usando el nombre de archivo usercontrol o el tipo de usercontrol. El método está en la clase System.Web.UI.TemplateControl.

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

pesar de que su base no es una página ASPX, puede hacer que se heredan de la misma clase marco como si se tratara:

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

} 
+0

Eso es lo que he hecho, no conozco los detalles, pero de alguna manera el espacio de nombres ASP simplemente no está disponible a menos que esté escribiendo en un página aspx real. –

+0

¿tiene todas las referencias que necesita en la página base? Me gusta "using System.Web.UI.WebControls" – Geoff

+0

Sí, estoy seguro. –

0

Suponiendo SpiffyControl y SpiffyPage están en el mismo espacio de nombres:

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

Puede mover su SpiffyControl en una biblioteca - esta le dará un espacio de nombres más preciso y también le permitirá usarlo en otros proyectos.Sin embargo, esto es un poco complejo: ya tiene un UserControl hecho, pero no funcionará en una biblioteca. UserControls usa el modelo de código subyacente; están compuestos por dos archivos, un marcado y un código. Las bibliotecas no parecen ser compatibles con el modelo de código subyacente, por lo que no puede usar un UserControl. Necesita convertir ese control de usuario a un control web.

Cree un nuevo proyecto de biblioteca de clases. Abra las propiedades del proyecto y establezca AssemblyName y Default Namespace en SpiffyLibrary.

Ahora tenemos que convertir su UserControl. Primero, crea una clase en tu nueva biblioteca. Llámelo SpiffyControl, al igual que su control de usuario existente, pero hágalo heredar de System.Web.UI.WebControls.CompositeControl.

En segundo lugar, abra el código para su control de usuario existente y copie todo dentro de la clase. Luego regrese a la nueva clase y péguelo todo adentro. Si está utilizando cualquiera de los eventos de control estándar, es posible que deba cambiar el nombre de esas funciones para anular los eventos apropiados. Por ejemplo,

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

... ... debe convertirse en

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

    base.OnLoad(e); 
} 

Tercera (esta es la parte compleja) que necesita para reproducir todo el margen que tenía en archivo de anotación de SpiffyControl. No puede simplemente copiarlo; en su lugar, debe anular la función CreateChildControls() heredada de CompositeControl. Cada etiqueta que tenía en el marcado necesita ser instanciada como una variable y agregada a la colección de Controles de la clase. Así, por ejemplo, si el archivo de marcado existente era la siguiente:

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

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

... entonces su función de nuevos CreateChildControls debería tener este aspecto:

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

Tenga en cuenta que los controles se declaran como campos de clase; de esa manera, son visibles para todos los métodos de clase.

Una vez que haya convertido su marcado, compile el proyecto de la biblioteca y agréguelo a las referencias para su proyecto de sitio web. A continuación, abra el archivo web.config de su sitio web y busque la sección system.web/pages/controls. Esta sección ya debería tener una etiqueta como esta:

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

Esa etiqueta es lo que le permite usar los controles de las bibliotecas centrales de ASP. Así que vamos a añadir uno igual, para nuestra biblioteca:

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

Ahora en cualquier lugar de su sitio, se puede poner en el marcado de esta manera:

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

... y que la voluntad cargue su control web desde su biblioteca personalizada.

0

También puede definir manualmente el espacio de nombre de su SpiffyControl. Para hacer esto, necesita cambiar el código subyacente y el marcado.

En el código subyacente, ajuste su clase en un bloque de espacio de nombres. Entonces, si su clase fue

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

... luego cámbiela a ...

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

Luego, en el marcado, ajuste su encabezado @Control. Así que si su cabecera era @Control

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

... luego cambiarlo a ...

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

Suponiendo que va a correr en un ambiente que apoya la reflexión que podría utilizar estos métodos.

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

y lo llaman así:

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

    SetValue(control, propertyName, value); 

Si se llama a una gran cantidad es posible que desee almacenar en caché la información de tipo.

También podría considerar la creación de una interfaz ISpiffyControl en el ensamblaje en el que se encuentra SpiffyPage, luego haga que su usuario lo implemente.

2

¿Están bromeando conmigo, sé que formularios web ASP.NET fue una experiencia dolorosa, pero nadie ha oído hablar del atributo nombre de clase, es decir

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

Por lo tanto, el original ejemplo:

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

Se ha tocado, pero una interfaz debería funcionar. Hacer ISpiffyControl con cualesquiera miembros/métodos, implementarlo en el SpiffyControl.ascx.vb y hacer esto en el 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 
0

Si leo esto correctamente, el cartel original tenía el problema en el que no puede acceder a la Tipo de la clase base de la cual la página principal deriva en su UserControl, y no al revés. Da la casualidad que tuve el mismo problema.

En mi caso, todas las páginas de mi proyecto heredan de la misma base, por lo que no me preocupa el hecho de que mi control de usuario tenga conocimiento de la clase base de la página. Disculpas a los arquitectos

Sin embargo, resolví el problema con una combinación de 1) usando una clase base para mi control de usuario, y 2) empleando un espacio de nombres común. Esto permitió la visibilidad que necesitaba, ya que, por alguna razón, la clase base del control de usuario aún no podía ver el tipo de clase base de la clase base de las páginas.

De todos modos, solo pensé en compartir mis resultados en caso de que ayuden a alguien más en el futuro.

HTH.

Cuestiones relacionadas