Cargar UserControl en un HTTPHandler y setear sus propiedades dinámicamente

Necesite reutilizar un UserControl dentro de un controlador HTTP, pero me di cuenta que no puede realizar una referencia a mi UserControl como lo hacemos en las paginas .aspx, es decir no tenia forma de definir el tipo de control como para luego acceder a sus propiedades.

Veamos algo como veníamos utilizando al UserControl….

Cuando en una pagina .aspx normal (por llamarla de alguna manera) necesitamos utilizar un UserControl, lo arrastramos hacia ella y listo.
Se nos registra con la directiva @Register el UC para poder utilizarlo, junto con el Tagname y el TagPrefix
image 

@Register:
Crea una asociación entre un prefijo de etiqueta y un control personalizado, lo que proporciona a los desarrolladores una forma concisa de hacer referencia a controles personalizados en un archivo de aplicación ASP.NET (incluidos páginas Web, controles de usuario y páginas principales).

Cuando necesitamos utlizarlo en algun propiedad o llamarlo desde el codigo debemos, podemos hacer uso de la directiva @Reference, para que se compile y agregue la referencia (es como registrar las antiguas DLL’s…)
Asi podremos utilizar la referencia al objeto externo compilado y a sus miembros públicos desde el archivo actual.
image

@ Reference:
Indica que otro control de usuario, archivo de código fuente de página o archivo arbitrario que se encuentra en alguna ruta de acceso virtual debe compilarse dinámicamente y vincularse al archivo ASP.NET actual (página Web, control de usuario o página principal) en el que se declara esta directiva.

 

Y en el HTTPHandler (.ashx) ??

Aquí no tenemos estas directivas u opciones…

image

La forma que encontré es mediante Reflection, obteniendo el tipo

Dim miCtrl As UserControl = pagina.LoadControl("~/modulos/ucEjemplo.ascx")
Dim tipoControl As Type = miCtrl.GetType

Y luego mediante esta formidable clase.. puedo Obtener una propiedad  y setearla

Dim propiedadExpedienteId As System.Reflection.PropertyInfo = tipoControl.GetProperty("ExpedienteId")
Dim valor As Int32 = 4442
propiedadExpedienteId.SetValue(miCtrl, valor, Nothing)
     

Luego ejecuto la pagina que tengo como ayuda (que me permitió cargar el control con LoadControl) y lo guardo en un StringWriter

Dim wr As System.IO.StringWriter = New System.IO.StringWriter()
HttpContext.Current.Server.Execute(pagina, wr, False)

 

 

 

Aquí esta el código completo del ejemplo:

 

Public Class Ejemplo : Implements IHttpHandler
    
    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        context.Response.Clear()
        context.Response.ContentType = "text/html"
        context.Response.Write(ObtenerContenido)
        context.Response.End()
    End Sub
 
    
    Private Function ObtenerContenido() As String
        
        Dim pagina As Page = New Page()
        Dim miCtrl As UserControl = pagina.LoadControl("~/modulos/ucEjemplo.ascx")

        Dim tipoControl As Type = miCtrl.GetType
        
        'Ejemplo: valor 4442
        tipoControl.GetProperty("ExpedienteId").SetValue(miCtrl, 4442, Nothing)
       
        pagina.Controls.Add(miCtrl)

        Dim wr As System.IO.StringWriter = New System.IO.StringWriter()
        HttpContext.Current.Server.Execute(pagina, wr, False)

        Dim cadenaSalida As String = wr.ToString()
        
        wr.Close()
        
        Return cadenaSalida
    End Function
   
    
    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

    
End Class

 

Enviar el contenido de un UserControl por WebService

Idem a lo anterior:

    <WebMethod()> _
    <System.Web.Script.Services.ScriptMethod()> _
    Public Function Actuaciones(ByVal contextKey As String) As String 

        Dim pagina As Page = New Page()
        Dim miCtrl As UserControl = pagina.LoadControl("~/modulos/Ejemplo.ascx") 

        Dim tipoControl As Type = miCtrl.GetType 

        Dim propiedadExpedienteId As System.Reflection.PropertyInfo = tipoControl.GetProperty("ExpedienteId")
        Dim valor As Int32 = Convert.ToInt32(contextKey)
        propiedadExpedienteId.SetValue(miCtrl, valor, Nothing) 

        pagina.Controls.Add(miCtrl) 

        Dim wr As System.IO.StringWriter = New System.IO.StringWriter()
        HttpContext.Current.Server.Execute(pagina, wr, False) 

        Dim cadenaSalida As String = wr.ToString() 

        wr.Close() 

        Return cadenaSalida 

    End Function

Este webmethod es especial por el parámetro que recibe, ya que lo utilizo dentro de un TabPanel para obtener los datos dinámicamente…
En este ejemplo seteo la propiedad DynamicContextKey con un valor para test, la idea es que luego se establezca por código cuando se carga la pagina.

<cc2:TabPanel ID="TabPases" runat="server" 
               HeaderText="Historial de Pases" 
               DynamicContextKey="4442" 
               DynamicServiceMethod="Pases" 
                DynamicServicePath="/servicios/expedientesWS.asmx">
         <ContentTemplate>
                 
</ContentTemplate>

 

 

 

Enlaces

  • @ Register
    Crea una asociación entre un prefijo de etiqueta y un control personalizado, lo que proporciona a los desarrolladores una forma concisa de hacer referencia a controles personalizados en un archivo de aplicación ASP.NET (incluidos páginas Web, controles de usuario y páginas principales).
  • @ Reference
    Indica que otro control de usuario, archivo de código fuente de página o archivo arbitrario que se encuentra en alguna ruta de acceso virtual debe compilarse dinámicamente y vincularse al archivo ASP.NET actual (página Web, control de usuario o página principal) en el que se declara esta directiva.
  • Introducción a los controladores HTTP
  • IHttpHandler (Interfaz)
    Define el contrato que ASP.NET implementa para procesar de forma sincrónica las solicitudes Web HTTP mediante controladores HTTP personalizados.
  • Object.GetType (Método)
    Obtiene el objeto Type de la instancia actual.
  • Type (Clase)
    Representa declaraciones de tipo: tipos de clase, tipos de interfaz, tipos de matriz, tipos de valor, tipos de enumeración, parámetros de tipo, definiciones de tipo genérico y tipos genéricos construidos abiertos o cerrados.
  • Type.GetProperty (Método)
    Obtiene una propiedad específica del objeto Type actual.
  • PropertyInfo (Clase)
    Descubre los atributos de una propiedad y proporciona acceso a los metadatos de la misma.
  • PropertyInfo.SetValue (Método)
    Establece el valor de propiedad del objeto especificado en el valor indicado.

Obtener la cultura del cliente… CurrentCulture/CurrentUICulture

Preguntaron en los grupos de noticias de asp.net, como obtener la cultura del browser del cliente.
(y como tenia en el tintero/borrador este articulo.. complete con algunos enlaces.. y a publicarlo)

El primer ejemplo:   

CultureInfo.CurrentCulture.Name()

Nos devuelve la cultura actual del proceso (Thread) actual, pero no del navegador del cliente.

Posible y «tentativa» solución

Una solución de ejemplo podría ser leyendo la propiedad UserLanguages del Request:

Private Function ObtenerCultura() As String
    Dim culInfo As System.Globalization.CultureInfo
    culInfo = System.Globalization.CultureInfo.CreateSpecificCulture(HttpContext.Current.Request.UserLanguages(0))
    Return culInfo.ToString
End Function

Como la propiedad UserLanguages devuelve un arreglo de idiomas (cadena de caracteres) del cliente.. seleccionamos el primer item, y armamos nuestra cultura

Luego tendríamos que asignarla si queremos que sea la cultura por defecto para por ejemplo manipular fechas, presentar recursos.
En el ejemplo anterior habría que agregar esta línea:

Thread.CurrentThread.CurrentCulture = culInfo

Aquí como se dice, estamos «explicitando» la cultura a utilizar en base al lenguaje del cliente (que leímos del browser)

 

Solución en NET 2.0

Hace un par de meses nos comentaba esto José M. Alarcón en un articulo:
  – Cambio automático de cultura en ASP.NET 2.0 (by José M. Alarcón)

Como y donde podemos cambiar?

    1. Por Pagina
      image
      Pero podemos hacer que sea automático que capture el valor del cliente
       image
    2. Toda la aplicación: 
      Donde si no es en el web.config…
      image
      O automáticamente…
      image
    3. Por código… como ya lo implementamos en el ejemplo mas arriba

 

 

 

Como lo probamos?

En el IE7 tenemos la posibilidad de cambiar el idioma de preferencia….
Tools >> Internet Options >> General >> Button Languages

image

 

Si ahora navegamos por el sitio podemos obtener, mediante el código detallado mas arriba:

  • CultureInfo.CurrentCulture.Name(): es-AR
  • ObtenerCultura(): fr-FR

 

 

 

Por si queda algo en el tintero… CurrentCulture/CurrentUICulture ??

(from MSDN…)

CurrentCulture:

La propiedad CultureInfo.CurrentCulture es una configuración de cada subproceso que determina los formatos predeterminados para la fecha, hora, moneda y números, el criterio de ordenación de texto, las comparaciones de cadenas y la distinción entre mayúsculas y minúsculas. La propiedad CurrentCulture no es una configuración de idioma. Sólo contiene datos relacionados con la configuración estándar para una región geográfica.
Nota: Aquí la palabra clave seria Internacionalización, donde se puede visualizar un dato sensible (por ejemplo la fecha) en forma correcta dependiendo de la cultura.

CurrentUICulture:

La propiedad CultureInfo.CurrentUICulture es una configuración para cada subproceso que devuelve la referencia cultural de la interfaz de usuario actual. La clase ResourceManager utiliza esta propiedad para buscar recursos específicos de la referencia cultural en tiempo de ejecución.
Nota: La palabra clave aquí es la Localización de los recursos

Casi siempre debería ser igual ambas, pero hay momentos que no poseemos por ejemplo al paquete de idioma (Language Pack) de NET 2.0 (que es un paquete de localización para el idioma español por ejemplo)

Y allí la UI no queda con el idioma para por ejemplo traducir los mensajes de algunos webcontrol que definimos, porque «físicamente» no esta instalado.

 

No queda muy claro? y no… aquí un muy buenos articulo de  

 

 

Enlaces

Varios DefaultButton para un mismo formulario web

En una pregunta de hoy en los foros de MSDN el tema era como tener mas de un DefaultButton por formulario, ya que queria que en un UserControl de Busqueda sea un boton el que se ejecute al presionar Enter sobre el texbox de busqueda, y tambien otra parte de la pagina funcione similarmente pero con otros controles.

Para refrescar la memoria podemos decir que por formulario en paginas ASP.NET podemos tener un DefaultButton, algo así:

<body>
  <form id="Form1"
        defaultbutton="Boton1"
        defaultfocus="TextBox1"
        runat="server">
    ...
 
Pero aquí lo que buscamos es tener mas de un botón…y no uno general (y por que no incluso un DefaultControl…! lo dejo para pensar)
La idea es algo así… que dependiendo en que Textbox estoy presionando ENTER se realiza el postback de un botón determinado
image
 
 
Hoy encontré en este articulo, interesante para la ultima opción

 

 

 

OPCION 1: Simple JS

EJEMPLO 1.1

Los controles:

<asp:TextBox ID="txtBusqueda" runat="server" />    
<asp:Button id="btnBusqueda" runat="server"  />

En el codebehind:       

txtBusqueda.Attributes.add("onkeypress", "PresionTeclaBusqueda();")

El JS:

<script type="text/javascript">
   function PresionTeclaBusqueda(){
     if (event.keyCode==13){
        document.getElementById('<%=btnBusqueda.ClientId%>').click();
     }
  }
</script> 

 

EJEMPLO 1.2

Algo mas general seria enviar el ID del botón a realizar el click:  

txtBusqueda.Attributes.add("onkeypress", "ClickBoton('" & btnBusqueda.ClientId & "');") 
El código de script del lado del cliente
<script type="text/javascript">
   function ClickBoton(botonId){
     if (event.keyCode==13){
        document.getElementById(botonId).click();
     }
  }
</script> 

 

 

 

OPCION 2: Función para asignar botón por defecto al un control

Como ya comente mas arriba, aqui encontre una funcion muy sencilla, general y mas completa que la opcion 1.
Es un método que escribe en la pagina un script (si ya no esta registrado, para no escribirlo dos veces), y asigna un boton por defecto

La idea es enviarle a cada control su «boton por defecto»
      

SetDefaultButton(Me.Page, TextBox1, Button1)
SetDefaultButton(Me.Page, TextBox2, Button2)
SetDefaultButton(Me.Page, TextBox3, Button3)

Aquí el código en VB.NET para ASP.NET 2.0 (en el articulo original es para C#, y para ASP.NET 1.1)

Private Sub SetDefaultButton(ByVal pagina As Page, ByVal textControl As TextBox, ByVal botonPorDefecto As Button)

        'Armando script
        Dim script As New Text.StringBuilder

        script.Append("<SCRIPT language=""javascript"" type=""text/javascript"">" & vbCrLf)
        script.Append("<!--" & vbCrLf)
        script.Append("function fnTrapKD(btn, event){" & vbCrLf)
        script.Append("" & vbCrLf)
        script.Append(" if (document.all){" & vbCrLf)
        script.Append("  if (event.keyCode == 13){" & vbCrLf)
        script.Append("   event.returnValue=false;" & vbCrLf)
        script.Append("   event.cancel = true;" & vbCrLf)
        script.Append("   btn.click();" & vbCrLf)
        script.Append("  }" & vbCrLf)
        script.Append(" }" & vbCrLf)
        script.Append("" & vbCrLf)
        script.Append(" else if (document.getElementById){" & vbCrLf)
        script.Append("  if (event.which == 13){" & vbCrLf)
        script.Append("   event.returnValue=false;" & vbCrLf)
        script.Append("   event.cancel = true;" & vbCrLf)
        script.Append("   btn.click();" & vbCrLf)
        script.Append("  }" & vbCrLf)
        script.Append(" }" & vbCrLf)
        script.Append("" & vbCrLf)
        script.Append(" else if(document.layers){" & vbCrLf)
        script.Append("  if(event.which == 13){" & vbCrLf)
        script.Append("   event.returnValue=false;" & vbCrLf)
        script.Append("   event.cancel = true;" & vbCrLf)
        script.Append("   btn.click();" & vbCrLf)
        script.Append("  }" & vbCrLf)
        script.Append(" }" & vbCrLf)
        script.Append("}" & vbCrLf)
        script.Append("// -->" & vbCrLf)
        script.Append("</SCRIPT>" & vbCrLf)

        'Registrando script
        pagina.ClientScript.RegisterStartupScript(pagina.GetType(), "FUNCIONBOTONDEFECTO", script.ToString)
     
        'Asignando evento funcion al evento onkeydown
        textControl.Attributes.Add("onkeydown", "fnTrapKD(" + botonPorDefecto.ClientID + ",event)")

    End Sub

 

Aquí la AppWeb con la opcion 2
 

 

 

 

 

 

Enlaces

Adobe TV y Adobe Media Player (AMP)

[Para mis amigos diseñadores y amantes de la creatividad]
Al mejor estilo de «Evangelización por video» nos brinda programas de TV para todo tipo de perfil Fotógrafos, Diseñadores, Editores de Video, Desarrolladores

http://tv.adobe.com

AdobeTV_001

Podremos visualizarlo por tema/perfil

AdobeTV_002

Ya teníamos varios en YouTube para nuestro deleite, tutoriales, ejemplos, etc. pero aquí es la misma empresa quien nos ofrece.
Ya que son videos de tutoriales de programas como Lynda.com, Dr. Brown’s Photoshop Laboratory (ver enlace Programs en el menú de Adobe TV)

AdobeTV_003

y al mejor estilo y formato podemos visualizarlo (y por supuesto embeberlo o linkearlo)

AdobeTV_004

 

Pero lo interesante es este enlace

AdobeTV_005

 

Pero… para Adobe Media Player, necesitamos previamente  Adobe AIR (mas adelante puede ser que escriba sobre este tema)
AdobeAir_001

AdobeAir_002

 

Entonces… descargando 11Mb… :S

AdobeAir_003

Luego instalando…

AdobeAir_004

Y si llego el momento. Instalando el Adobe Media Player… un nuevo reproductor en mi computadora ;(

AdobeAir_005

AdobeAir_006

Una vez instalado el Adobe Media Player, nos pregunta si queremos agregar este programa/video al reproductor… (quien no necesito un administrador parecido pero de YouTube, yo tengo uno instalado como plugin en Firefox)

AdobeAir_007

y ta tenemos no solo el programa/show/video/ en nuestro reproductor

AdobeAir_008

Sino el canal completo con todos sus episodios…

 

 

 

 

AMP: Adobe Media Player

Para el que lo quiere descargar:
http://get.adobe.com/amp/
Es de 1.4MB … mmm más 11MB de Adobe Air

AdobeMediaPlayer_001

 

Al final la UI como siempre muy buena…sencilla

AdobeMediaPlayer_002 

 

 

Que puedo hacer??

Y.. reproducir vídeos

AdobeMediaPlayer_003

Textual de la ayuda de AMP

(…)In addition to Flash (FLV and FV4) video, Adobe Media Player supports video files containing H.264 and HE-AAC content, including .mov, .mp4, .mp4v, .m4v, .3gp, .3gpp2 and .3g2 video formats, allowing you to effectively view and manage your personal video library through a single application.

Note: These file types can contain media other than H.264 and HE-AAC formats or encodings of H.264 or AAC that aren’t compatible with Flash and Adobe Media Player. Adobe Media Player will do its best to play back files of these types, but only files ending with FLV and F4V are guaranteed to play.(…)

 

me gusto reproducir .FLV

AdobeMediaPlayer_004

 

Importar otros formatos

No encontré la forma de exportar otros formatos…
En la ayuda online de AMP

 

Videos online

Y claro… necesitaba también Adobe un reproductor de video/televisión como

 

Así que también tenemos un catalogo online

AdobeMediaPlayer_007

Pero son programas grabados (no es en vivo ) … o no encontré.

AdobeMediaPlayer_008 

 

 

 

 

Sugerencia de podcast de video

Anteriomente los videos «by Adobe», me los descargaba como podcast de video desde iTunes

Mediante la interfaz de iTunes podremos buscar podcast de video sobre estos temas.. y se descargan automáticamente en nuestro reproductor preferido 😉

iTunesPodcast_001

 

Ejemplos

iTunesPodcast_002

 

 

El único que encontré de .NET en el directorio de podcast de iTunes

http://www.dotnetrocks.com/

iTunesPodcast_003

Utilizar VB.NET y C# al mismo tiempo en la carpeta APP_CODE de ASP.NET

Justamente me acaban de preguntar como utilizar estos lenguajes de este modo. Este articulo lo escribí en Septiembre de 2006, y lo había colgado de otro en MisTrucos.NET, en mi Space.
Aquí en Geeks.ms ya lo comento José M. Alarcón, en base a cualquier lenguaje:

Quise postearlo aquí para que quede en el historial 😉

Aquí va…

Bueno he de aquí que por las cosas de la vida y componentes que reutilice y falta de tiempo necesite utilizar clases en VB.NET y C# dentro de la carpeta APP_CODE en un proyecto web en VS2005..

Coloque mis clases Clase1.vb y Clase2.cs en la carpeta y genero el siguiente error:
Exception Details: System.Web.HttpException: The files ‘/WebSite1/App_Code/Clase2.cs’ and ‘/WebSite1/App_Code/Clase1.vb’ use a different language, which is not allowed since they need to be compiled together.

Pero no creí que tuviera esta limitación así que me dedique a verificar como seria el proceso de indicarle el tipo de lenguaje que se utiliza en la famosa carpeta… por defecto el directorio APP_CODE puede contener clases del mismo lenguaje, pero se puede agregar subdirectorios e indicar a ASP.NET que los compile separadamente.

Cual es el proceso?….

  1. Primeramente insertar en la seccion de <compilation> del webconfig estas lineas que agregan un coleccion de subdirectorios que asp.net debe compilar en forma separada, o mejor dicho de forma agrupada por estos directorios…
    <codeSubDirectories>
            <add directoryName=»App_Code_VB»/>
            <add directoryName=»App_Code_CS»/>
    </codeSubDirectories>
  2. Luego tendías que crear estos directorios
    /App_Code/App_Code_VB
    /App_Code/App_Code_CS

  3. Y llegando al final, distribuir nuestras clases de VB.NET dentro de la carpeta correspondiente App_Code_VB, y las de C# en la carpeta App_Code_CS

 

Algunos datos interesantes (desde los enlaces a MSDN):

  • El orden de generación se deduce del orden descendente de la colección codeSubDirectories.
  • El directorio App_Code se genera en último lugar.
  • Los subdirectorios se muestran en el orden en el que se debe compilar el código para cumplir sus dependencias, pero…
  • No existen dependencias de orden de generación, lo que significa que este orden no es significativo.
  • No hay ningún elemento codeSubDirectories predeterminado configurado para ASP.NET.
  • Puede haber cualquier número de subdirectorios

 

Enlaces:

Obtener UserId desde el UserName en ASP.NET

La pregunta de «Donde obtengo el ID del Usuario?»…(para un amigo que esta migrando aplicaciones de ASP Clásico) pero para todo que se inicia, …aquí una pequeña ayuda.

Si vemos que nos brinda ASP.NET 2.0 con el espacio de nombre My.User

image

Vemos que el famoso USERID que tanto nos gusta y requerimos no lo tenemos a alcance de la mano…

 

Como Obtenerlo?

Aquí hacemos uso clases Membership y MembershipUser para obtener el usuario y luego preguntar por su ProviderUserKey

 

image

Ejemplo

        Dim usuario As MembershipUser = Membership.GetUser
        Dim usuarioID As String = usuario.ProviderUserKey.ToString

        Dim usuario2 As MembershipUser = Membership.GetUser(My.User.Name)
        Dim usuarioID2 As String = usuario2.ProviderUserKey.ToString

 

Y como estamos en este tema…Hace unos días coloque mi granito de arena para Dot Net Tip of the Day (este mismo articulo) , y parece que muchos lo hicieron ya que da un servicio de los tips que van acumulando de la comunidad…

.NET Tips & Tricks Community (ALPHA)

http://dotnettipoftheday.org/SuggestedTips.aspx

image

 

 

Enlaces