Marc Rubiño

ASP.NET, C#, AJAX.NET, JavaScript, etc.

Cargar Controles de Usuario dinámicamente ASP.NET AJAX

En respuesta a la consulta de Julitogtu generada en los foros de MSDN, he generado un ejemplo práctico de su consulta. Ya que le aconsejé la utilización de controles de usuarios generados dinámicamente en detrimento de los famosos iframes.

Pero esta generación se complica especialmente si quieres cargar estos controles con llamadas asíncronas, por el hecho de que estos controles se generan en el servidor.

Lo primero que tenemos que hacer es tener los controles de usuario bien organizados para poderlos utilizar desde el cliente con llamadas a métodos de Página o servicios web, de esta manera evitaremos todo el tráfico que genera los updatePanels.

image

Yo he generado tres controles de usuarios distintos para probar diferentes posibilidades que nos podemos encontrar en la vida real.

  • Un formulario: Encontraremos los típicos controles asp.net para introducir los datos de un formulario.
  • Login: Dentro de este control tendremos otro ASP.NET para la creación de usuarios registrados.
  • Grid: Control de usuarios donde mostraremos los datos de una base de datos con un GridView.

Después en la página principal de la aplicación tendremos un TreeView para seleccionar los controles a cargar y un div donde se mostrara la información.

Cargaremos la lista manualmente y le indicaremos a los nodos que en cuanto se selecciones ejecuten el método de página que devolverá el control de usuario en formato texto, para esto solo le pasaremos el nombre de la función y el identificador del control que queremos cargar en el enlace.

private void CargarDatos()
{
  Dictionary<string,string> controles = 
                  new Dictionary<string,string>();
  controles.Add("FormControl", "Datos de contacto");
  controles.Add("GridControl", "Lista de Productos");
  controles.Add("LoginControl", "Iniciar sesión");

  foreach (KeyValuePair<string, string> value in controles)
  {
    TreeNode node = new TreeNode();
    node.Text = value.Value;
    node.NavigateUrl = "BLOCKED SCRIPTPageMethods.CargarControl('"
       + value.Key +"', CargarControlOK, CargarControlKO);";
    TreeView1.Nodes.Add(node);
   }
}

Código de servidor:

La página contará de un Método de Página que se encargara de cargar el control de Usuario y transformara el control en el resultado HTML que es el que se devolverá al cliente para que se añada al div y muestre el control en el navegador.

[WebMethod]
public static string CargarControl(string tipoControl)
{
   StringWriter stringWriter = new StringWriter();
   Page page = new Page();
   System.Web.UI.HtmlControls.HtmlForm form = 
     new System.Web.UI.HtmlControls.HtmlForm();
   form.ID = "__t";
   page.Controls.Add(form);

   switch (tipoControl)
   {
     case "FormControl":
     {
       UserControls_Form cForm = 
        (UserControls_Form)page.LoadControl("~/UserControls/FormControl.ascx");
       form.Controls.Add(cForm);
       HttpContext.Current.Server.Execute(page, stringWriter, false);
       break;
     }
     case "GridControl":
     {
       UserControls_Grid cGrid = 
        (UserControls_Grid)page.LoadControl("~/UserControls/GridControl.ascx");
       cGrid.Inicializar("Condiments");
       form.Controls.Add(cGrid);
       HttpContext.Current.Server.Execute(page, stringWriter, false);
       break;
     }
     case "LoginControl":
     {
       UserControls_Login cLog = 
        (UserControls_Login)page.LoadControl("~/UserControls/LoginControl.ascx");
       form.Controls.Add(cLog);
       HttpContext.Current.Server.Execute(page, stringWriter, false);
       break;
    }
  }
  //Limpiamos el formulario ASP
  string html=(stringWriter != null)?stringWriter.ToString():"<h2>Sin Datos</h2>";
  html = html.Substring(html.IndexOf("<div>"));
  html = html.Substring(0, html.IndexOf("</form>"));

  return html;
}

Al utilizar Métodos de Página o Servicios web no existe el estado y tenemos que generar una página ASP.NET y ejecutarla para que se generen los controles y poder mostrar su resultado HTML. Esta idea esta extraída del fenomenal artículo de Scott Guthrie:  Ajax views. Pero uno de los inconvenientes que nos encontramos con esta técnica es que obtenemos el formulario completo, por ese motivo hay que limpiar el HTML y eliminar el rastro del formulario.

Otra cosa que tenemos que tener en cuenta es que podemos controlar en que momento tenemos que cargar los datos en el control, yo en este caso he generado una función que se encarga de inicializar y cargar los datos filtrados directamente con un objeto SQLDataSource.

public void Inicializar(string Categoria)
{
   SqlDataSource1.SelectParameters.Add("CategoryName", Categoria);
   SqlDataSource1.SelectCommand = 
    @"SELECT [ProductID], [ProductName], [CategoryName] 
";
      FROM [Alphabetical list of products] 
         WHERE ([CategoryName] = @CategoryName)
   GridView1.DataSourceID = "SqlDataSource1";
   GridView1.DataBind();
}

Código Cliente:

Tendremos dos funciones, una para recoger el resultado y cargar el div “utilizando JQuery” y el otro mostraremos  un mensaje en el caso que ocurra un error.

function CargarControlOK( resultado ){
    if ($(".divBase").length)
       $(".divBase")[0].innerHTML = resultado;        

}
function CargarControlKO(resultado){

    alert("Error al cargar el menú: " + resultado._message)
}

Resultado:

El resultado obtenido en esta práctica, es que al seleccionar un nodo del treeView se genera una llamada asíncrona que no carga todo la página y carga en un div el control de usuario que se especifica en la llamada, de esta manera podemos reutilizar toda la funcionalidad que nos aportan estos controles.

Podéis pasar el código de una página aspx de una forma muy sencilla a un control de usuario sin tener que repicar todo el código Cómo: Convertir páginas de formularios Web Forms en controles de usuario ASP.NET

image

 

image

 

image

 

Espero que esta información sea de utilidad.

Ejemplo:  http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/mrubino.Ejemplos/UserControlsAjax.zip 

Posted: 12/4/2010 16:35 por Marc Rubiño | con 8 comment(s)
Comparte este post:

Comentarios

supercordobes ha opinado:

Te hago una consulta, mas que nada porque ya he trabajo de manera extensiva con el manejo user controls cargados dinámicamente y hay un problema recurrente que es que se debe realizar entre cada postback al servidor se debe crear y ubicar cada UC de nuevo, por lo que es una fuente de errores.

Con los update panels desaparece este problema? Los eventos dentro del UC se lanzan correctamente?

Saludos

# April 13, 2010 7:38 PM

Marc Rubiño ha opinado:

El problema no se origina al lanzar los eventos, sino al intentar cargar el estado de los controles asp.net que se han modificado en el cliente y hace que la pagina lance un error al validar el viewState de la misma.

Es un problema dificil de solucionar por la misma filosifia de los formularios web. "Eso en MVC no pasa"

Una posible solución "no muy elegante" es eliminar el viewState para evitar el error, pero te obliga a recargar la información de la página que quieres mantener.

Elimino el viewState justo cuando se va a pintar la página.

protected override void Render(HtmlTextWriter writer)

{

    StringBuilder stringBuilder = new StringBuilder();

    StringWriter stringWriter = new StringWriter(stringBuilder);

    HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);

    base.Render(htmlWriter);

    string yourHtml = stringBuilder.ToString();

    System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"(<input type=""hidden"" name=""__VIEWSTATE"" id=""__VIEWSTATE"" value=""[a-zA-Z0-9\+=\\/]+"" />)");

    yourHtml = reg.Replace(yourHtml, "");

    writer.Write(yourHtml);

}

# April 14, 2010 1:08 AM

00Ceballos ha opinado:

Buenas noches, trabajo con controles de asp y los inserto en un updatepanel...

*****************************************************

<asp:UpdatePanel   ID="upScrollTest" runat="server" UpdateMode="Conditional" >

    <ContentTemplate>          <asp:PlaceHolderID="PlaceHolder1"               runat="server"></asp:PlaceHolder>

    </ContentTemplate>

    <Triggers>

         <asp:AsyncPostBackTrigger  ControlID="TV_Menu_P" />

    </Triggers>      

</asp:UpdatePanel>

*****************************************************

luego en el codigo vb del aspx principal donde voy a meter los controles ahi tengo un select case para cada nodo del treeView, hace todo perfecto carga el control cada que clickeo en uno de los nodos, el pequeño problema que tengo es cuando voy a empezar a utilizar el control, doy click en un boton de alta que tiene mi control, hace un postback y no hace nada, ya despues puedo empezar a trabajar bien con el control, solo quisiera saber como eliminar ese postback que debe hacer al principio.

# May 26, 2010 4:40 AM

Andrew ha opinado:

Hi, all

so i wanna know how this declarration is done??

UserControls_Form cForm = (UserControls_Form)Page.LoadControl("~/UserControls/FormControl.ascx");

# September 1, 2010 1:18 AM

Bessewell ha opinado:

Hola,

Algo parecido deseo implementar con respecto a crear varios objetos dinámicamente de cualquier procedencia y tipo para luego rastrear el contenido de los mismos al seleccionados o llenados por el usuario.

Por ejemplo, imaginemos que en un caso hipotético quiero crear 10 textbox en una página ASPX de forma dinámica y a cada uno de ellos darle un compartamiento diferente uno del otro, como también asignarle un ID diferente para poderlos identificar.  Cómo puedo yo rastrear el contenido de los mismos partiendo del hecho de que todos ellos fueron creados en tiempo de ejecución ?

Gracias.

# January 18, 2011 7:14 PM

Josman ha opinado:

Hola a todos! Estuve leyendo este articulo y esta muy bueno, solo que tengo el mismo problema del usuario 00Ceballos, "...uso el control, hace un postback y no hace nada, ya despues puedo empezar a trabajar bien con el control, solo quisiera saber como eliminar ese postback que debe hacer al principio..." alguien sabe como solucionar este problema?

# March 8, 2011 11:21 PM

choco_larenga ha opinado:

Deshabilita el autopostback del boton.

# July 18, 2011 3:36 PM

ellerysammy ha opinado:

Estoy trabajando con controlea ascx, en uno de dichos controles estoy cargando el menu de la aplicacion, pero cada que hago click en un nodo del arbol al cambiar de pagina el menu se vuelve a cargar, como hago para que no se recargue sino que solo lo haga al principio y luego permanezca.??

Gracias de antemano por cualquier ayuda

# February 7, 2012 6:00 PM