2009-08-24 12 views
5

Actualmente estoy trabajando con una parte de mi aplicación que usa Controles de usuario web dinámicos y estoy teniendo problemas para encontrar la mejor manera de volver a crear instancias de los controles en la devolución de datos usando ViewState u otro método que no funciona No necesito que consulte una base de datos en cada devolución.¿Cómo puedo volver a crear instancias de controles de usuario dinámicos de ASP.NET sin utilizar una base de datos?

Básicamente lo que tengo en mi página es un control de usuario que contiene un panel para contener una cantidad variable de controles de usuario secundarios y un botón que dice "Agregar control" cuya función es bastante clara.

El control de usuario secundario es bastante simple; es solo un botón de eliminar, un menú desplegable y un control del selector de tiempo organizado en una fila. Cada vez que el usuario hace clic en el botón Agregar control en el control principal, se agrega una nueva 'fila' al panel que contiene los controles secundarios.

Lo que me gustaría hacer es poder agregar y quitar controles a esta colección, modificar valores y realizar cualquier operación que necesite hacer 'in-memory' sin tener que hacer ninguna llamada a una base de datos. Cuando termine de agregar controles y poblar sus valores, me gustaría hacer clic en "guardar" para guardar/actualizar todos los datos de los controles en una base de datos a la vez. Actualmente, la única solución que he encontrado es simplemente guardar los datos en la base de datos de cada publicación y luego usar las filas almacenadas en el archivo db para volver a crear instancias de los controles en la devolución de datos. Obviamente, esto obliga al usuario a guardar cambios en la base de datos en contra de su voluntad y, en caso de que quiera cancelar el trabajo con los controles sin guardar sus datos, se debe realizar un trabajo adicional para garantizar que las filas previamente comprometidas se eliminen.

Según lo que he aprendido sobre el uso de controles dinámicos, sé que es mejor agregar los controles a la página durante la etapa Init del ciclo de vida y luego llenar sus valores en la etapa de carga. También aprendí que la única manera de asegurarse de que puede persistir el estado de visualización del control es asegurarse de darle a cada control dinámico un ID único y asegúrese de asignarle el mismo ID exacto al volver a crear el control. También aprendí que el ViewState no se carga hasta después de la etapa Init en el ciclo de vida. Aquí es donde reside mi problema. ¿Cómo guardo y recupero los nombres de estos controles si no puedo usar ViewState y no deseo realizar ninguna llamada a una base de datos? ¿Es posible utilizar este tipo de manipulación en memoria/lotes de valores utilizando ASP.net?

Cualquier ayuda es muy apreciada,

Mike

+0

Un poco tarde, pero lo hago así: http://stackoverflow.com/a/15346332/52898 – Tommy

Respuesta

3

Se puede almacenar el mínimo de lo que usted necesita saber recrear los controles en una colección celebrada en sesión. La sesión está disponible durante las fases de inicio de la página.

Aquí hay un ejemplo para usted. Se compone de:

Default.aspx, cs
- Panel para almacenar usuario controla
- "Añadir botón de control" que añadirá un control de usuario cada vez que se hace clic en

TimeTeller.ascx, cs
- tiene un método llamado SetTime que establece una etiqueta en el control a un tiempo específico.

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DynamicControlTest._Default" %> 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

<html xmlns="http://www.w3.org/1999/xhtml" > 
<head runat="server"> 
    <title></title> 
</head> 
<body> 
    <form id="form1" runat="server"> 
    <div> 
     <asp:Panel ID="pnlDynamicControls" runat="server"> 
     </asp:Panel> 
     <br /> 
     <asp:Button ID="btnAddControl" runat="server" Text="Add User Control" 
      onclick="btnAddControl_Click" /> 
    </div> 
    </form> 
</body> 
</html> 

Default.aspx.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.UI; 
using System.Web.UI.WebControls; 

namespace DynamicControlTest 
{ 
    public partial class _Default : System.Web.UI.Page 
    { 
     Dictionary<string, string> myControlList; // ID, Control ascx path 

     protected void Page_Load(object sender, EventArgs e) 
     { 

     } 

     protected override void OnInit(EventArgs e) 
     { 
     base.OnInit(e); 

     if (!IsPostBack) 
     { 
      myControlList = new Dictionary<string, string>(); 
      Session["myControlList"] = myControlList; 
     } 
     else 
     { 
      myControlList = (Dictionary<string, string>)Session["myControlList"]; 

      foreach (var registeredControlID in myControlList.Keys) 
      { 
       UserControl controlToAdd = new UserControl(); 
       controlToAdd = (UserControl)controlToAdd.LoadControl(myControlList[registeredControlID]); 
       controlToAdd.ID = registeredControlID; 

       pnlDynamicControls.Controls.Add(controlToAdd); 
      } 
     } 
     } 

     protected void btnAddControl_Click(object sender, EventArgs e) 
     { 
     UserControl controlToAdd = new UserControl(); 
     controlToAdd = (UserControl)controlToAdd.LoadControl("TimeTeller.ascx"); 

     // Set a value to prove viewstate is working 
     ((TimeTeller)controlToAdd).SetTime(DateTime.Now); 
     controlToAdd.ID = Guid.NewGuid().ToString(); // does not have to be a guid, just something unique to avoid name collision. 

     pnlDynamicControls.Controls.Add(controlToAdd); 

     myControlList.Add(controlToAdd.ID, controlToAdd.AppRelativeVirtualPath); 
     } 
    } 
} 

TimeTeller.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TimeTeller.ascx.cs" Inherits="DynamicControlTest.TimeTeller" %> 
<asp:Label ID="lblTime" runat="server"/> 

TimeTeller.ascx.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.UI; 
using System.Web.UI.WebControls; 

namespace DynamicControlTest 
{ 
    public partial class TimeTeller : System.Web.UI.UserControl 
    { 
     protected void Page_Load(object sender, EventArgs e) 
     { 

     } 

     public void SetTime(DateTime time) 
     { 
     lblTime.Text = time.ToString(); 
     } 

     protected override void LoadViewState(object savedState) 
     { 
     base.LoadViewState(savedState); 
     lblTime.Text = (string)ViewState["lblTime"]; 
     } 

     protected override object SaveViewState() 
     { 
     ViewState["lblTime"] = lblTime.Text; 
     return base.SaveViewState(); 
     } 
    } 
} 

Como puede ver, todavía tengo que administrar el estado de visualización interno de mi control de usuario, pero la bolsa viewstate se guarda en la página y se devuelve al control en la devolución de datos. Creo que es importante tener en cuenta que mi solución es muy similar a la de David. La única diferencia importante en mi ejemplo es que está usando session en lugar de viewstate para almacenar la información de control. Esto permite que las cosas sucedan durante la fase de inicialización. Es importante tener en cuenta que esta solución consume más recursos del servidor, por lo tanto, puede no ser apropiado en algunas situaciones, dependiendo de su estrategia de escala.

+0

Me gusta esa idea, porque todo lo que realmente me preocupa es la identificación (y tal vez el orden) de los controles que estoy tratando de cargar. Voy a buscar un banco de pruebas rápido para esa solución y me pondré en contacto contigo. Gracias! – mclark1129

+0

Espero que pueda hacerlo funcionar. He tenido que hacer algo similar en el pasado. Lo mejor es volver a colocar los controles en la página en la fase de inicio porque así es como se espera que transcurra el ciclo de vida del control. Estoy seguro de que la solución de David funciona, pero me preocupa la capacidad de mantenimiento a largo plazo. He hecho cosas así también y finalmente se convierte en un escenario de "lucha contra el marco". Eventualmente puede encontrarse con algo que simplemente no funcionará si lo agrega tan tarde en el ciclo de vida. –

+0

Daniel, He progresado un poco con el uso de la solución de sesión, pero he topado con dos inconvenientes. Una es que, a pesar del hecho de que los controles se están reagrupando en la etapa Init(), el estado de la vista no se está restaurando. Tenía la impresión de que todo lo que necesito establecer en el control dinámico cuando se está reubicando es la identificación correcta para recuperar los datos de View State, ¿hay algo que me falta? Además, los datos de la sesión persisten incluso si no estoy usando una devolución posterior. Simplemente establecería la sesión para anular cuándo! Page.IsPostBack, pero la bandera no se establece hasta después de la etapa Init(). – mclark1129

3

he hecho esto en el pasado. No he tenido que hacer esto desde los días de .NET 1.1, pero el principal elimina el mismo.

Lo hice en Page_Load no Init tiene que volver a cargar los controles que ha creado en el ciclo de la última página.

Primero necesita realizar un seguimiento de los controles que ha creado en cada ciclo de página. Esto incluye tipo, nombre, etc. .

Luego, en cada carga de página debe reconstruirlos.

Para ello, vuelva a crear el control, asignándole exactamente la misma identificación, agréguelo al lugar de la muestra en la página y finalmente en el ViewState ["LoadedControl"] al tipo de control.

Aquí está el código que utilicé, solo lo hice con los controles de usuario que creé. No he intentado esto con un control ASP.NET, pero creo que funcionaría igual.

En este caso tengo un ArrayList de trillizos (tenga en cuenta que esto es .NET 1.1) y el primer elemento era una identificación de PageView.Es posible que no lo necesite para su aplicación.

protected void Page_Load(object sender, System.EventArgs e) 
{ 
    //********************************************************** 
    //* dynCtlArray will hold a triplet with the PageViewID, * 
    //* ControlID, and the Control Name      * 
    //********************************************************** 

    ArrayList dynCtlArray = (ArrayList)this.ViewState["dynCtlArray"]; 
    if (dynCtlArray != null) 
    { 

     foreach (object obj in dynCtlArray) 
     { 
      Triplet ctrlInfo = (Triplet)obj; 

      DynamicLoadControl(ctrlInfo); 
     } 
    } 
} 

private void DynamicLoadControl(Triplet ctrlInfo) 
{ 
    // ERROR HANDLING REMOVED FOR ANSWER BECAUSE IT IS NOT IMPORTANT YOU SHOULD HANDLE ERRORS IN THIS METHOD 

    Control ctrl = this.LoadControl(Request.ApplicationPath 
     + "/UC/" + (string)ctrlInfo.Third); 

    ctrl.ID = (string)ctrlInfo.Second; 

    // Create New PageView Item 
    Telerik.WebControls.PageView pvItem = this.RadMultiPage1.PageViews[(int)ctrlInfo.First]; 
    pvItem.Controls.Add(ctrl); 

    /****************************************************** 
    * The ControlName must be preserved to track the * 
    * currently loaded control       * 
    * ****************************************************/ 
    ViewState["LoadedControl"] = (string)ctrlInfo.Third; 
} 
private void RegisterDynControl(Triplet trip) 
{ 
    ArrayList dynCtlArray = (ArrayList)this.ViewState["dynCtlArray"]; 

    if (dynCtlArray == null) 
    { 
     dynCtlArray = new ArrayList(); 
     this.ViewState.Add("dynCtlArray", dynCtlArray); 
    } 

    dynCtlArray.Add(trip); 

} 

En algún método en su página

// Create new Control 
Control ctrl = Page.LoadControl("../UC/MyUserControl.ascx"); 

// . . . snip .. . 

// Create Triplet 
Triplet ctrlInfo = new Triplet(0, ctrl.ID, "MyUserControl.ascx"); 
// RegisterDynControl to ViewState 
RegisterDynControl(ctrlInfo); 

// . . . snip .. . 

Para acceder a los controles para salvar allí la información que va a tener que hacer un this.Page.FindControl('');

+0

David, Muchas gracias por la respuesta. Todavía no he tenido la oportunidad de probar su solución, pero al analizarla tuve una pregunta: He utilizado Page_Load en el pasado para cargar controles dinámicos, pero eso generalmente me da problemas ya que el evento Load viene después el Estado de vista se ha cargado (de ahí su capacidad para acceder a la matriz de ID de control). Como resultado, los valores ingresados ​​por el usuario se pierden en la devolución de datos debido al hecho de que el control aún no se agregó a la página cuando se cargó el estado de la vista. ¿La forma en que agrega estos controles a la página carga manualmente su estado de visualización? – mclark1129

+0

Page_Load debe tener acceso al estado de vista. Pero sí se cargará allí estado de vista. Por ejemplo, diga que está cargando dinámicamente un cuadro de texto. En mi proceso, le está diciendo a la página 'Oye, preste atención a estos controles' para que luego de que se carguen pueda obtener el valor del cuadro de texto, o cualquiera de su información de estado de vista.Tienes que decirle a la página que fueron creados manualmente, de lo contrario, la página nunca lo sabrá. –

1

Implementé una página muy similar al ejemplo de Daniel, pero no pude usar Session debido a restricciones técnicas. Sin embargo, descubrí que al usar un campo para la página, podía publicar y recuperar su valor durante el evento Page_Init.

Cuestiones relacionadas