Ejemplo de Control de Usuario con dos Combos relacionados

Los controles de usuario son muy útiles a la hora de reutilizar código.
Si dedicamos un poco de tiempo a desarrollarlos con cuidado, pueden simplificar muchísimo la tarea del programador.
Aquí les dejo un ejemplo donde genero un control de usuario llamado Locación enlazando dos Combos o DropDownList (Región y Territorio). Luego muestro la forma de utilizarlo en cada página donde se necesite.

Vamos directamente al código que es parte del Proyecto MoniMisiDemo que pueden bajarse de aquí:

<%@ Register Assembly="MoniMisi2" Namespace="MoniMisi2" TagPrefix="MM2" %>
<MM2:ProveedorDatosServidor ID="Region" runat="server">
</MM2:ProveedorDatosServidor>
<MM2:ProveedorDatosServidor ID="Territorio" runat="server">
</MM2:ProveedorDatosServidor>
<table>
    <tr>
        <td>
            Region :
        </td>
        <td>
            <MM2:Combo ID="ComboRegion" runat="server" Valor="RegionID"
                Descripcion="RegionDescription" Lleno="true">
            </MM2:Combo>
        </td>
        <td>
            Territorio :
        </td>
        <td>
            <MM2:Combo ID="ComboTerritorio" runat="server" Valor="TerritoryID"
                Descripcion="TerritoryDescription" Lleno="false">
            </MM2:Combo>
        </td>
    </tr>
</table>

Esto es el ASCX del control, y como ven, simplemente coloco los proveedores de datos y los combos.
No he enlazado los proveedores a ningún Webmethod ni los combos a ningun proveedor. Esto se debe a que el control de usuario genera los id de cliente dinamicamente, agregando a cada id hijo su propio id.

Esta característica nos obliga a hacer casi todas las asignaciones de forma dinámica. Y lo haremos de la siguiente manera:

Imports MoniMisi2

Partial Class Controles_de_Usuario_Locacion
  Inherits System.Web.UI.UserControl
  Dim WebMethodRegionValor As String = ""
  Public Property WebMethodRegion() As String
    Get
      Return WebMethodRegionValor
    End Get
    Set(ByVal value As String)
      WebMethodRegionValor = value
    End Set
  End Property
  Dim WebMethodTerritorioValor As String = ""
  Public Property WebMethodTerritorio() As String
    Get
      Return WebMethodTerritorioValor
    End Get
    Set(ByVal value As String)
      WebMethodTerritorioValor = value
    End Set
  End Property

  Public Shared Function ManejoRegion(ByVal pContrato As MoniMisi2.ProveedorDatosServidor.Contrato) As MoniMisi2.ProveedorDatosServidor.Contrato
    Dim Contrato As MoniMisi2.ProveedorDatosServidor.Contrato = pContrato
    Dim Region As New NorthWindTableAdapters.RegionTableAdapter
    If Contrato.Operacion.Nombre = "Leer" Then
      Contrato = ProveedorDatosServidor.LeerFilas(Contrato, Region.Adaptador())
    End If
    Return Contrato
  End Function
  Public Shared Function ManejoTerritorio(ByVal pContrato As MoniMisi2.ProveedorDatosServidor.Contrato) As MoniMisi2.ProveedorDatosServidor.Contrato
    Dim Contrato As MoniMisi2.ProveedorDatosServidor.Contrato = pContrato
    Dim Territorio As New NorthWindTableAdapters.TerritoriesTableAdapter
    If Contrato.Operacion.Nombre = "Leer" And Not Contrato.Filtro Is Nothing Then
      Contrato = ProveedorDatosServidor.LeerFilas(Contrato, Territorio.Adaptador(Contrato.Filtro))
    End If
    Return pContrato
  End Function

  Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
    ScriptManager.RegisterClientScriptBlock(Me.Page, Me.GetType, "ActualizaTerritorio", "function ActualizaTerritorio(pRegion,pTerritorio) {pTerritorio.control.Refrescar(true, pRegion.value);} ", True)
    ScriptManager.RegisterClientScriptBlock(Me.Page, Me.GetType, "InicializaTerritorio", "function InicializaTerritorio(pRegion, pTerritorio) {if (pRegion.options.length == 1) {pRegion.value = pRegion.options[0].value;ActualizaTerritorio(pRegion, pTerritorio)}}", True)
    Me.ComboRegion.IdDatos = Me.Region.ClientID
    Me.ComboRegion.Attributes.Add("onchange", "ActualizaTerritorio(document.getElementById('" + Me.ComboRegion.ClientID + "'),document.getElementById('" + Me.ComboTerritorio.ClientID + "'))")
    Me.ComboRegion.Poblando = "function() {InicializaTerritorio(document.getElementById('" + Me.ComboRegion.ClientID + "'),document.getElementById('" + Me.ComboTerritorio.ClientID + "'))}"
    Me.ComboTerritorio.IdDatos = Me.Territorio.ClientID
    Me.Region.WebMethod = WebMethodRegion
    Me.Territorio.WebMethod = WebMethodTerritorio

  End Sub
End Class

Lo primero que hago es generar dos propiedades (WebMethodRegion y WebMethodTerritorio) que servirán al usuario de este control para especificar a través de que WebMethods se accederá a los datos de Región y Territorio respectivamente.

Lo segundo es generar las funciones que traen los datos desde el SQL Server, y llenan los proveedores. Verán que las declaré con una firma idéntica a los WebMethods que usamos para los proveedores de datos. Esto se debe a que estas dos funciones serán invocadas desde la página por los WebMethods declarados en la propiedades del primer paso.
(si no entiende porque hago esto vean esto)

El tercer y último paso es asignar los id que correspondan para que todo quede bien referenciado. Y generar dos pequeñas funciones en JavaScript para que los Territorios se actualicen cada vez que cambie la Región.

Vuelvo a mostrar el código de estas dos funciones para explicarlas brevemente.

function ActualizaTerritorio(pRegion,pTerritorio) 
{
   pTerritorio.control.Refrescar(true, pRegion.value);
}

Esta es la función que refresca el combo de Territorio cada vez que cambiamos el combo de Región. Simplemente invocamos el método Refrescar con dos parámetros.
El primero indica que debe limpiar el combo, y el segundo que vuelva a consultar al servidor llevando el Id de la región que fue seleccionada.

  InicializaTerritorio(pRegion, pTerritorio) 
  {
    if (pRegion.options.length == 1) 
    {
      pRegion.value = pRegion.options[0].value;
      ActualizaTerritorio(pRegion, pTerritorio)
    }
  }

Esta función se invoca por cada registro que es introducido en el combo de Región. Lo único que hacemos aquí es preguntar si es el primer registro y actualizamos el combo de Territorios. Esto nos asegura que la primera vez que se llena el combo de regiones aparecerán los territorios de la primera región aunque el usuario no la haya seleccionado explícitamente.

Por último verán que uso ScriptManager.RegisterClientScriptBlock para registrar esta dos funciones. Esto lo hago para que solo sean incluidas una sola vez, aunque ponga varios controles de usuario del mismo tipo en la página.

Ahora veremos una página donde utilizamos este control un par de veces.

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Locacion.aspx.vb" Inherits="Locacion" %>

<%@ Register Assembly="MoniMisi2" Namespace="MoniMisi2" TagPrefix="MM2" %>
<%@ Register Src="Controles de Usuario/Locacion.ascx" TagName="Locacion" TagPrefix="uc1" %>
<!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>
        <MM2:ScriptManager ID="ScriptManager" runat="server">
        </MM2:ScriptManager>
        <table style="border :solid 1px black">
            <tr>
                <td  style=" background-color:Silver ">
                    Desde
                </td>
            </tr>
            <tr>
                <td>
                    <uc1:Locacion ID="Desde" runat="server" WebMethodRegion="ManejoRegion" WebMethodTerritorio = "ManejoTerritorio"/>
                </td>
            </tr>
            <tr>
                <td  style=" background-color:Silver ">
                    Hasta
                </td>
            </tr>
            <tr>
                <td>
                    <uc1:Locacion ID="Hasta" runat="server" WebMethodRegion="ManejoRegion" WebMethodTerritorio = "ManejoTerritorio"/>
                </td>
            </tr>
        </table>
    </div>
    </form>
</body>
</html>

Simplemente tiramos un ScriptManager y dos controles Locación definiendo qué WebMethods usaremos para la comunicación asincrónica. Verán que uso los mismos WebMethods en ambas instancias. Esto es para que los Proveedores compartan los datos y no se generen llamadas o uso de memoria innecesario.

Imports System.Web.Services

Partial Class Locacion
  Inherits System.Web.UI.Page

  <WebMethod()> _
Public Shared Function ManejoRegion(ByVal pContrato As MoniMisi2.ProveedorDatosServidor.Contrato) As MoniMisi2.ProveedorDatosServidor.Contrato
    Return Global.ASP.controles_de_usuario_locacion_ascx.ManejoRegion(pContrato)
  End Function

  <WebMethod()> _
Public Shared Function ManejoTerritorio(ByVal pContrato As MoniMisi2.ProveedorDatosServidor.Contrato) As MoniMisi2.ProveedorDatosServidor.Contrato
    Return Global.ASP.controles_de_usuario_locacion_ascx.ManejoTerritorio(pContrato)
  End Function
 
End Class

Aquí simplemente capturamos la llamada con el WebMethod y le pasamos el trabajo al Control de Usuario, que es el que sabe como recuperar los datos.

Bueno … eso es todo.
Solo queda decir que si es necesario enlazar un tercer combo en cascada, deberíamos agregar un proveedor, un combo y enlazarlo al de Territorio tal y como lo hicimos entre el los dos primeros.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s