2010-07-09 20 views
7

He desarrollado un control personalizado que amplía ListBox. La idea es que el control 'recuerda' modificaciones a sus elementos que ocurrieron en el lado del cliente, p. como resultado de una solicitud AJAX.Control personalizado ASP.NET: ¿cuándo se llama a LoadPostData()?

La forma en que funciona es que el control también representa una entrada oculta, y el resultado de la solicitud AJAX se almacena en la entrada oculta. Esto se publica de nuevo, y el método LoadPostData() del control busca la entrada oculta, y si la entrada oculta tiene datos, crea la colección ListItem de ella.

Esta perfectamente funciona siempre y cuando el usuario ha realizado una selección de la lista. Si no lo han hecho, no se llama al método LoadPostData() y, en consecuencia, no se crea la nueva colección ListItem. (Establecí esto usando el depurador)

Supongo que el método LoadPostData solo se invoca si la recopilación de datos POST incluye datos correspondientes a UniqueID del control (es decir, el atributo 'nombre' en HTML). Si el usuario no ha realizado una selección en el cuadro de lista, no se incluye nada en los datos de la publicación para UniqueID del cuadro de lista y no se llama a LoadPostData(). ¿Es eso correcto?

¿Alguien puede sugerir cómo puedo asegurarme de que mi método LoadPostData() personalizado de ListBox se llame en cada devolución, independientemente de si el usuario ha hecho una selección?

Gracias de antemano - Estoy realmente atrapado con este.

David

Respuesta

1

He establecido que el método LoadPostData() no se llama a menos que los datos de la publicación contengan un elemento con el mismo nombre que UniqueID del control. [Editar: llamar a Page.RegisterRequiresPostback durante Init() supera esto.] Puedo ver por qué, pero es bastante limitante.

He superado el problema al no manejarlo durante el método LoadPostData(). En cambio, lo he manejado en un método al que llamo OnLoad().

Dos cosas tienen que tener en cuenta cuando se utiliza este enfoque:

1) Ya no tiene acceso al objeto postCollection NameValueCollection que se pasa en el método LoadPostData() como argumento. Esto significa que tiene que extraer los datos de la publicación de la colección Request.Form, que es un trabajo un poco más difícil. 2) Debido a que OnLoad() ocurre después del código de procesamiento ViewState, deberá configurar manualmente el SelectedValue después de crear los ListItems. Si no lo hace, si el cuadro de lista está lleno a través de AJAX y el usuario hace una selección, la selección se perderá.

Espero que esto ayude a alguien en el futuro.

3

suena muy extraño estar trabajando sólo cuando se selecciona un elemento. Una forma rápida de verificar si se está invocando LoadPostData sería habilitar el rastreo y colocar lo siguiente en IPostBackDataHandler.LoadPostData (...).

Page.Trace.Write("My control", "LoadPostData"); 

Si ese es el caso, usted debe asegurarse de que usted tiene lo siguiente:

Page.RegisterRequiresPostBack(this) in OnInit 

Aquí está una muestra de control completo.

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Web.UI; 
using System.Web.UI.WebControls; 
using System.ComponentModel.Design; 
using System.ComponentModel; 
using System.Web.UI.Design; 

namespace Controls 
{ 
    public sealed class ExtendedListBoxDesigner : ControlDesigner 
    { 

     public override string GetDesignTimeHtml() 
     { 
      StringBuilder sb = new StringBuilder(); 
      sb.Append("<div>My designer</div>"); 
      return sb.ToString(); 
     } 

    } 

    [DesignerAttribute(typeof(ExtendedListBoxDesigner), typeof(IDesigner))] 
    public class ExtendedListBox : ListBox, INamingContainer, IPostBackDataHandler 
    { 
     bool IPostBackDataHandler.LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection) 
     { 
      Page.Trace.Write("ExtendedListBox", "LoadPostData"); 
      return true; 
     } 


     protected override void OnInit(EventArgs e) 
     { 
      Page.RegisterRequiresPostBack(this); 
      base.OnInit(e); 
     } 

     protected override void RenderContents(HtmlTextWriter writer) 
     { 
      base.RenderContents(writer); 
      writer.Write(string.Format("<input type=\"hidden\" name=\"{0}_dummy\" value=\"alwaysPostBack\">", this.ID)); 
     } 

    } 
} 

y la página se ve así.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ControlSamlpe._Default" Trace="true" %> 
<%@ Register Assembly="Controls" Namespace="Controls" TagPrefix="sample" %> 
<!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>Untitled Page</title> 
</head> 
<body> 
    <form id="form1" runat="server"> 
    <div> 
    <sample:ExtendedListBox runat="server" ID="extListBox"></sample:ExtendedListBox> 
    <asp:Button runat="server" ID="go" /> 
    </div> 
    </form> 
</body> 
</html> 

Al hacer clic en Ir, debería ver "LoadPostData" en la traza.

+0

No es en absoluto extraño que funciona sólo cuando se selecciona un elemento. El método LoadPostData() solo se invoca si postdata contiene un elemento con el mismo nombre que el UniqueID del control, lo que no sucederá si el usuario no ha seleccionado un elemento. – David

+0

Bueno, ¡mi primera reacción fue que me pareció extraño cuando comencé a escribirla! :) ¿Intentó agregar el campo ficticio en RenderContents? De esta forma, siempre se llamará a LoadPostData. –

+0

Gracias por su sugerencia sobre la creación de un elemento 'falso' con el mismo atributo de nombre que UniqueID del control. De alguna manera veo lo que quieres decir. En mi opinión, no es tan limpio como la solución que he ofrecido. ¿Qué piensas? – David

8

estoy un poco tarde en el salto en esto, pero, sólo para referencia en el futuro, así es como he logrado algo similar ...

Mi control es un árbol que utiliza plantillas para los nodos. El problema con el que lidiaba con esto era cómo capturar los cambios del lado del cliente al estado expandido/colapsado de los nodos. Lo que terminó funcionando fue:

En CreateChildControls, agregue el campo oculto a la colección de controles de mi control raíz.

protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding) 
{ 
    ... 
    _cdExpanded = new HiddenField(); 
    _cdExpanded.ID = "cdExpanded"; 
    this.Controls.Add(_cdExpanded); 
    ... 
} 

En llamada OnInit

protected override void OnInit(EventArgs e) 
{ 
    ... 
    Page.RegisterRequiresPostBack(this); 
    ... 
} 

En LoadPostData buscar un valor en la recogida posterior que coincide con el UniqueID (no ClientID) del campo oculto:

public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection) 
{ 
    ... 
    string cdExpanded = postCollection[_cdExpanded.UniqueID]; 
    ... 
} 

Dentro de la clasificación para los nodos individuales, tengo un código que rellena los eventos onclick de mis botones de alternar con una llamada a una función de JavaScript que toma el ID del control base y los nodos individuales como argum ents.

string ToggleScript 
    { 
     get 
     { 
      return "ToggleNode('" + this.ClientID + "', '" + _TreeRoot.ClientID + "');"; 
     } 
    } 
    protected override void Render(HtmlTextWriter writer) 
    { 
     ... 
     if (this.HasChildren) 
     { 
      writer.AddAttribute("onclick", ToggleScript); 
     } 
     ... 
    } 

Esto hace que sea para que encontrar el campo oculto es bastante fácil a través de getElemenById:

function ToggleNode(nodeID, treeID) { 
var cdExpanded = document.getElementById(treeID + "_cdExpanded"); 
... 
} 

El Javascript a continuación, modifica el valor del campo oculto como sea necesario para el evento que se ha producido. Cuando regresemos al servidor, puedo analizar el contenido de este campo y modificar el estado de control según sea necesario antes de que se vuelva a representar. (Nota: en realidad yo uso 3 campos ocultos para el seguimiento de diferentes eventos, pero el concepto es el mismo)

Esperanza esto ayuda a otros en el futuro ...

+0

Gracias por su contribución. – David

+3

OnInit era exactamente lo que faltaba aquí, ¡gracias! – dividius

Cuestiones relacionadas