Archive for the ‘Código C#’ Category

Autodesk AutoCAD Map 2011 y .NET Framework 4.0

Hace unos cuantos meses, cuando me dispuse a realizar mis primeros módulos en AutoCAD Map 2011, me puse en la disyuntiva de que versión del .NET Framework debería utilizar; dado que recientemente, migramos todo el proyecto GIS de Catastro que estamos desarrollando al .NET Framework 4.0 y Visual Studio 2010; la primera opción era la de utilizar la versión 4.0 del .NET Framework, pero el temor principal era la compatibilidad de AutoCAD Map 2011 con la última versión del .NET Framework.

Llegado a este punto, la única manera de despojarnos de estos temores es haciendo las usuales pruebas, así que me dispuse a programar mi clásico “Hola Mundo”, y grande fue mi sorpresa al encontrarme con este hermoso error de AutoCAD que parecía confirmar todos mis temores.

ErrorEnsambladoNoSoportado

la siguiente línea del mensaje de error fue el que me hizo sudar frio: “This assembly is built by a runtime newer than the currently loaded runtime and connot be loaded”, parecía indicar claramente que el ensamblado que intentaba ejecutar había sido compilado por una versión del .NET Framework mas nueva de la que podía soportar AutoCAD Map 2011; pero esa interpretación inicial traducida en el calor del momento, no era correcta, ya que en realidad dice que el ensamblado que intento ejecutar es mas nuevo que el .NET Framework que actualmente esta cargado; entonces alentado con esta pisca de esperanza me puse en la labor de buscar como cargar el Framework 4.0 en AutoCAD Map 2011 para que pueda ejecutar el ensamblado de prueba que acababa de crear.

Como es usual en estos casos, luego de buscar en interminables sitios web, haber revisado muchos manuales de Autodesk, y haber leído innumerables foros, nadie podía decirme exactamente cual era la manera de solucionar mi problema, pero en muchos de esos lugares encontré pistas importantes, como por ejemplo la existencia del archivo “acad.exe.config”, si señores como los archivos de configuración que toda una vida usamos al desarrollar nuestras aplicaciones .NET, así que nada mejor que echarle una mirada a este archivo haber si encontramos alguna pista adicional.

acad.exe.config_original

Analizando detenidamente la primera sección de configuración, podemos leer claramente que “<supportedRuntime version=“v4.0”/>” está comentada. así que todo parece indicar que ahora podemos decir Bingo!!!.

Al realizar una instalación por defecto de AutoCAD Map 2011, esta sección de configuración siempre esta comentada, por tanto solo nos queda descomentar esta sección, de tal manera que el archivo de configuración “acad.exe.config”, que dicho sea de paso se encuentra en la carpeta de instalación de AutoCAD Map 2011, quede de la siguiente manera:

acad.exe.config_modificado

Con la configuración anterior, ahora podemos proceder a ejecutar nuevamente nuestro “Hola Mundo” y esta vez AutoCAD nos deja cargar el ensamblado sin ningún mensaje de error y permite mostrar en tan ansiado mensaje:

HolaMundo

Para concluir, seguramente que todos ya sabrán con que versión del .NET Framework estoy trabajando mis módulos de AutoCAD Map, por supuesto la 4.0 y no podría ser de otra manera que con C#. También cabe aclarar que si bien esta prueba la realicé con AutoCAD Map 2011, esto funciona igual para AutoCAD 2011, AutoCAD Civil 2011, etc. Para versiones anteriores de AutoCAD, queda como tarea para el lector, averiguar si esto funciona.

Anuncios

Comentarios XML con GhostDoc en C#

Ya sé, no tienen que decírmelo, hacer comentarios en el código aburre y ni que hablar de los comentarios XML donde uno tiene que estar recordando las etiquetas necesarias lo cual nos hace perder tiempo y productividad a la hora de programar (Excelente excusa, espero que me lo crea mi jefa).

El único problema es que existen herramientas para asistirnos en la laboriosa tarea de escribir nuestros amados comentarios XML, sí esos que hay que comenzar con “/// “. Una de esas herramientas se denomina como ya habrán adivinado GhostDoc, la existencia de este tipo de herramientas tira por los suelos nuestras mejores excusas para no escribir comentarios.

Bueno ahora que no tenemos más excusas nos vemos obligados a ver cuál es la funcionalidad de esta herramienta.

GhostDoc
 GhostDoc es un plug-in compatible tanto con Visual Studio 2005  y Visual Studio 2008 creado por Roland Weigelt, cuya utilidad es (Obligarnos a escribir comentarios), automatizar la escritura de comentarios XML, generando toda la estructura del comentario, lo único que tenemos que hacer es modificar el texto que necesitemos para que todo esté más entendible, gracias a este plug-in nos ahorramos de escribir una cantidad impresionantes de texto y podemos realizar los comentarios XML muy rápido; repito ahora ya no hay excusas.

¿Cómo Funciona?
La mejor manera para comprender su funcionamiento es con un pequeño ejemplo, donde mostraremos su utilidad. Asumamos que estamos escribiendo un sistema altamente complejo donde tenemos que implementar la clase calculadora de la siguiente manera:

ClaseCalculadora

Como verán es una clase muy compleja y por lo tanto casi imposible de usar si es que no se tiene la documentación de:

  • ¿Para qué sirve la clase?
  • ¿Para qué sirve cada método?
  • ¿Que representa cada parámetro?

Para proceder con nuestra tarea de documentación asistida tenemos que realizar los siguientes pasos:

  1. Descargar la versión que corresponda de GhostDoc de la siguiente dirección: Roland Weigelt’s  GhostDoc. Luego procedemos a instalarlo dejando los valores por defecto.
  2. Al iniciar Visual Studio se nos pedirá que seleccionemos algunas opciones como combinación de teclas para acceso directo, donde también pueden dejar los valores por defecto, para verificar que esté instalado el plug-in, revisar en el menú principal la opción “Tools”, bajo la que ahora debería estar GhostDoc.
  3. Ya tenemos todo listo así que nos queda comenzar a generar los comentarios XML, en primer lugar comentaremos la clase, para lo cual pulsamos clic derecho sobre el nombre de la clase y en el menú contextual seleccionamos: “Document this” (también podemos utilizar la combinación de teclas de acceso rápido, si es que dejaron los valores por defecto serán: ctrl + shift + D) 

    MenuContextual

    Esto genera la estructura del comentario completa incluida las etiquetas de resumen, poniendo el cursor en posición para escribir la descripción de la clase:

    ComentarioClaseGenerado

    A continuación solo nos queda escribir la descripción de la clase:

    ComentarioClaseEscrito

  4. Ahora nos toca generar los comentarios XML para los métodos, el proceso es el mismo, pulsamos clic derecho sobre el nombre del método y del menú contextual seleccionamos: “Document this”, lo cual genera toda la estructura necesaria para la descripción del método los parámetros de entrada y la salida del método, como se muestra en la siguiente figura:

    ComentarioMetodoGenerado

    Ahora solo nos queda escribir la descripción del método, los parámetros y el valor de salida, repetimos el procedimiento para todos los métodos.

     ComentarioMetodoEscrito

  5. Ahora ya tenemos nuestra clase completamente comentada, cabe aclarar que en este caso solo utilizamos comentarios para la clase y para métodos, pero también se pueden generar comentarios para otros miembros de clases como propiedades y eventos. GhostDoc también tiene numerosas opciones de configuración avanzadas, para las cuales tendrán que profundizar más en el estudio de esta herramienta.

¿Y para qué sirven esos comentarios XML?
Ahora ya tenemos nuestra clase completamente comentada, y ¿para qué nos sirve esto?, pues si pensamos en lo más inmediato, sirve para que las personas que revisen el código de la clase “Calculadora”  puedan guiarse por estos comentarios y entender la funcionalidad de los miembros de la clase, esta no es su única utilidad, también sirve para que los usuarios de nuestra clase “Calculadora” sepan cómo utilizar los miembros públicos de la clase, los comentarios XML que escribimos se presentarán como ayudas de IntelliSense al momento de escribir el código como se muestra en las siguientes figuras:

 IntelliSenseMetodo

 IntelliSenseParametros

La utilidad de los comentarios XML no termina aquí, en el siguiente artículo veremos cómo generar los archivos de ayuda a partir de estos comentarios XML.
Espero que esta excelente herramienta los motive a escribir más comentarios XML.

Enlaces
Página Oficial de GhostDoc: Roland Weigelt’s  GhostDoc

Desarrollo de Aplicaciones con el ORM .netTiers Parte I: Introducción

En un post anterior hace algún tiempo hablamos sobre los ORMs más conocidos entre los cuales mencionamos a .netTiers que es una plantilla para CodeSmith. En esta ocasión iniciaremos una serie de artículos para mostrar como utilizar este excelente ORM.

Introducción a netTiers
.netTiers es una plantilla de código abierto para CodeSmith escrito en C#. La intención de esta plantilla es asistir a los desarrolladores eliminando el código repetitivo, que tenemos que escribir cada vez que desarrollamos una aplicación con conexión a datos. Al mismo tiempo nos provee de un framework completo para comenzar a trabajar en las partes más importantes de nuestra aplicación, como son la capa de presentación, la lógica empresarial, el flujo de trabajo de nuestra aplicación, etc.

Conjunto de características actuales de netTiers
A continuación presentamos un listado de las características generales de .netTiers:

  • Genera una solución completa con proyectos separados y un framework basado en capas para su aplicación. Puede comenzar a trabajar con la aplicación inmediatamente terminado el proceso de generación.
  • Crea un conjunto completo de procedimientos almacenados que están especializados en el dominio de su aplicación. Este código se puede ejecutar como SQL en línea o como procedimiento almacenado, lo cual es configurable.
  • Genera automáticamente objetos de entidad y las relaciones entre estos objetos del dominio, basado en las tablas y relaciones de la base de datos.
  • Avanzado motor de validación de reglas de entidad, el cual puede utilizar las reglas creadas en el proceso de generación o utilizar delegados para cualquier regla de validación personalizada.
  • Las clases generadas incluyen dos partes, una parcial y una concreta, por lo tanto puede personalizar la lógica de cualquier clase sin que este código se sobrescriba cuando se ejecuta el proceso de generación nuevamente.
  • Utiliza una lista genérica personalizada para las colecciones (TList) que soporta toda la interfaz del modelo de componentes de .NET y más, y soporta enlace, ordenación y filtrado (Bindable, Sortable and Filterable).
  • Crea un sitio web completo, ya pre-configurado y listo para comenzar a escribir código conectable a datos inmediatamente.
  • Crea un conjunto completo de controles web de administración, que pueden servir como una consola de administración web completamente funcional para base de datos.
  • Crea un conjunto completo de controles DataSource con tipo para toda la API de acceso a datos, con soporte en tiempo de diseño, estos controles son similares a los ObjectDataSource, pero con más características propias del dominio.
  • Crea una API de servicios web completa para el dominio, perfecto para clientes .Net WinForms o dispositivos móviles, y su configuración es bastante simple.
  • Crea un conjunto completo de pruebas de unidad para los proveedores de datos y provee aproximadamente un 50% de cobertura. Estas pruebas pueden ser para nUnit o Visual Studio Team System.
  • El código que se genera está completamente comentado y listo para sus necesidades de documentación, además de seguir las directivas de Microsoft para la nomenclatura.
  • Cualquier código que se encuentre en una carpeta especial en cualquiera de los proyectos, que por defecto se llama App_Code, será automáticamente incluido en el proyecto generado.
  • Las consultas que se incluyen en la API de accedo a datos, se pueden realizar por: Clave primaria, clave foránea, índices, relaciones de muchos a muchos, todos, selección paginada con filtro y búsqueda; así también métodos de escritura en la base de datos como Inserción/Actualización/Eliminación.
  • Puede crear sus propios procedimientos almacenados personalizados, para las entidades y .netTiers generará los métodos que sirven como envoltorio de ese procedimiento almacenado y todo lo que se necesite, esto permite tener su propia lógica dentro de la API de .netTiers.
  • y mucho más.

Arquitectura de .netTiers
La arquitectura de .netTiers está basada en las guías de Microsoft patterns & practices, específicamente de: Designing Data Tier Components and Passing Data Through Tiers

Arquitectura Microsoft

Este concepto de capa de datos está compuesto por componentes de entidad del negocio personalizadas (que representan a los datos propiamente dichos), y componentes de lógica de acceso a datos (la lógica de persistencia).

Bases de datos y SQL
¿Cómo sabe .netTiers que generar? .netTiers utiliza el proveedor de metadata de CodeSmith que analiza una base de datos con el fin de crear un completo dominio de objetos, basado en las relaciones de la base de datos. El explorador de esquemas provee información sobre bases de datos, tablas, vistas, índices, procedimientos almacenados y más.

Buenas prácticas para diseñar bases de datos
En un mundo perfecto sería muy conveniente utilizar las siguientes recomendaciones para que la generación de código con .netTiers y CodeSmith sea óptima:

  • Los nombres de las tablas deben de estar en singular y en notación Pascal (primera letra de cada palabra compuesta en mayúsculas), ejemplo: Orden, DetalleOrden, Producto, etc.
  • Las descripciones para tablas, columnas y claves deben estar establecidas como atributos extendidos, para Microsoft SQL Server se utiliza la clave “MS_Description” para atributos extendidos.
  • .netTiers no asume relaciones, es necesario que se creen las relaciones de clave foránea a clave primaria específicamente en la base de datos.

Procedimientos almacenados personalizados
Existen muchos casos en los que se necesita extender la API de acceso a datos, sin perder las características de generación de la capa de datos. .netTiers ofrece la capacidad de escribir sus propios procedimientos almacenados, proporcionando la posibilidad de incrementar la funcionalidad que no ha sido generada automáticamente o las características que son especializadas de una aplicación en particular, un ejemplo sencillo sería el caso de necesitar la creación de un procedimiento almacenado personalizado para obtener un listado de productos con stock por debajo de un determinado límite. Cuando se inicia el proceso de generación, el explorador de esquemas de CodeSmith intenta descubrir toda la metadata para determinar el conjunto de resultados de un procedimiento almacenado así como también los parámetros que requiere tanto de entrada como de salida con sus respectivos tipos de datos, en base a esta información puede crear los métodos de las clases necesarias para realizar el mapeo.

Existen algunos casos en los que los procedimientos almacenados no retornan los resultados esperados, los procedimientos almacenados personalizados no funcionan cuando se utilizan tablas temporales dentro del procedimiento, esto se produce porque cuando el explorador de esquemas de CodeSmith está analizando la información no tiene los privilegios suficientes para crear las tablas temporales; una solución alternativa es utilizar variables de tabla en lugar de las tablas temporales.

Un detalle importante a tener en cuenta es que si queremos retornar entidades del dominio desde los procedimientos almacenados personalizados, necesitamos devolver en la consulta todos los datos de la tabla y en el orden correcto para que coincida con una entidad del dominio, cuando los resultados de un procedimiento almacenado personalizado no coincide con algún objeto entidad del dominio, se mapea como un DataSet genérico o como un objeto DataReader, lo cual es configurable.

Hasta aquí la parte introductoria de esta serie de artículos, en la que vimos una descripción un tanto más detallada de las bases de .netTiers, toda esta información fue obtenida de la documentación oficial de .netTiers que puede ser consultada en la siguiente dirección: .netTiers Documentation Wiki

Error de Codificación al Recuperar Datos con Caracteres Especiales Como la Letra Ñ de Gridview a un TextBox

Como es costumbre este post se origina con la consulta de una amiga, que me solicitó ayuda con un pequeño problema que se le presentó (el problema parecía tan pequeño que hasta se sonrojaba al hacer la consulta).

El Problema
Al recuperar los datos de un campo de la fila seleccionada en un GridView, a un control TextBox, manda caracteres extraños cuando se tratan de letras especiales como la “ñ” o las vocales con tilde, para recuperar los datos se utiliza el siguiente código en el evento RowCommand, del GridView.

protected void GridView1_RowCommand(object sender,
    GridViewCommandEventArgs e)
{
     if(e.CommandName == “Select”)
     {
        //Seleccionamos la Fila Actual
        GridView1.SelectedIndex =
            Convert.ToInt32(e.CommandArgument.ToString());

         //Recuperamos el valor de la segunda celda
        TextBox1.Text = GridView1.SelectedRow.Cells[1].Text;
     }
}

La Solución
Desde el inicio sospeché que el problema se trataba de la codificación de la página, recordé un post que había leído solo hace un momento donde se mostraba la forma de poner la codificación por defecto, seguí los pasos que se indicaban, pero no solucionó el problema.

Entonces hice la prueba cambiando el control TextBox por un control Label, grande fue mi sorpresa cuando todo funcionó a la perfección, entonces el problema estaba en el TextBox que por alguna razón no decodificaba los caracteres especiales.

Luego recordé que había una función para codificar texto que se utilizaba en seguridad, para evitar que el usuario ingrese caracteres especiales HttpUtility.HtmlEncode() y su correspondiente método para decodificarlo HttpUtility.HtmlDecode(). Como el problema es que los caracteres están codificados y por alguna razón extraña que no llego a entender no se decodifica al asignarlo a un TextBox, para solucionarlo tenemos que utilizar HttpUtility.HtmlDecode(), para decodificarlo manualmente.

protected void GridView1_RowCommand(object sender,
    GridViewCommandEventArgs e)
{
    if(e.CommandName == “Select”)
    {
        //Seleccionamos la Fila Actual
        GridView1.SelectedIndex =
            Convert.ToInt32(e.CommandArgument.ToString());

        //Recuperamos el valor de la segunda celda decodificándola
        TextBox1.Text =
            HttpUtility.HtmlDecode(GridView1.SelectedRow.Cells[1].Text);

    }
}

Un Momento de Reflexión (Reflection) en la Vida Diaria

Esta vez un problema que se me presento me obligó a reflexionar, reflexionar y reflexionar…, el caso es el siguiente:

Tengo una clase que representa a una entidad, mas específicamente hablando la clase es la que se genera utilizando la plantilla NetTiers para CodeSmith, que se corresponde uno a uno con las tablas de la base de datos y que expone mediante sus propiedades las columnas de la tabla correspondiente, lo que necesito es cargar automáticamente las propiedades de la clase a sus correspondientes controles (TextBox) de un WinForm.

Necesito crear un método general que funcione con cualquier tipo de entidad, y como no utilizaré DataBinding, lo que se me ocurrió es utilizar la propiedad Tag que tienen todos los controles para poner ahí el nombre de la propiedad de la clase entidad que le corresponde.

El siguiente paso es recorrer las propiedades del objeto entidad e ir copiando los valores en el componente que le corresponde, pero ¿cómo recorrer las propiedades de una clase?; esto fue lo que me llevo a la Reflexión.

Reflexión.- “La reflexión es uno de los pilares de .NET. Esta característica permite almacenar y obtener información en tiempo de ejecución sobre casi cualquier objeto o tipo presente en un módulo. Es gracias a esto que es posible implementar técnicas fundamentales como la recolección de basura o la serialización en distintos formatos. Y aunque es cierto que la mayoría de los entornos de programación modernos proporcionan algún tipo de RTTI (runtime type information, el pariente pobre de la reflexión), nunca antes se había visto un uso tan extenso y generalizado de este recurso como en .NET.”

La definición anterior la tomé prestada de la página de uno de mis maestros Ian Marteens, les recomiendo que consulten un artículo muy ilustrativo con ejemplo incluido sobre reflexión en el siguiente link: Calling Dr. Marteens: Reflexión.

Utilizando Reflexión podemos recorrer con facilidad las propiedades de una clase:

public void CargarDatosAControles(Object entidad, Type tipo,
    Control contenedor)
{
    foreach (PropertyInfo info in tipo.GetProperties())
    {
        Attribute atributo = Attribute.GetCustomAttribute(info,
            typeof(DataObjectFieldAttribute));
        if (atributo != null)
        {
            foreach (Control control in contenedor.Controls)
            {
                 if (control.Tag != null &&
                    control.Tag.ToString() == info.Name)
                {
                    control.Text = info.GetValue(entidad,
                        null).ToString();
                }//if
            }//foreach
        }//if
    }//foreach
}

El método que se presenta en el ejemplo, utiliza tres parámetros, el primero es una referencia a un objeto entidad, el segundo es el tipo de la clase entidad y el tercer parámetro es la referencia a un control que contiene a los controles (TextBox) donde queremos cargar los datos de la clase entidad, por ejemplo un Panel.

Para comenzar podemos obtener información de las propiedades de un tipo utilizando el método GetProperties() que nos devuelve una colección de objetos PropertyInfo que contiene la información de todas las propiedades de un tipo en este caso una clase entidad.

Un problema que se me presento es que cuando recorres las propiedades de un tipo, el método GetProperties() devuelve todos las propiedades, y resulta que la clase entidad con la que estoy trabajando aparte de las propiedades que se corresponden con las columnas de la tabla de base de datos que representa, tiene mas propiedades.

Entones tengo que discriminar cuales son las propiedades que corresponden a una columna de una tabla de base de datos y cuales no. Para hacer eso tengo que utilizar la información que proporcionan los atributos que se asocian a la declaración de las propiedades en el código fuente. Revisando el código de la clase entidad encuentro que las propiedades que me interesan tienen asociado a ellas un atributo llamado DataObjectFieldAttribute que indica que la propiedad es un campo de datos, la definición de la entidad luce así:

[DataObjectField(false, false, false, 300)]
public virtual System.String RazonSocial

Lo cual indica que la propiedad RazonSocial es un campo de datos, por los 4 parámetros del constructor del atributo podemos deducir que: no es clave primaria, no es un campo de identidad es decir autogenerado, no acepta valores nulos, y tiene una longitud de 300.

Ahora regresemos a nuestro método CargarDatosAControles, en el cual tenemos la siguiente instrucción:

Attribute atributo = Attribute.GetCustomAttribute(info,
    typeof(DataObjectFieldAttribute));

El código anterior obtiene un atributo de tipo DataObjectFieldAttribute a partir del objeto PropertyInfo de una propiedad.

Luego verificamos que el atributo obtenido sea diferente de null lo cual indica que la propiedad posee ese atributo y por tanto es un campo de datos, solo así procedemos a recorrer los controles del contendedor y buscar a que componente (TextBox) le corresponde la propiedad que estamos tratando, obtenemos el valor de la propiedad a través del método GetValue(entidad, null) de la clase PropertyInfo, para lo cual necesitamos como primer parámetro una instancia de la clase entidad y como segundo parámetro le pasamos null porque no estamos tratando con propiedades indexadas.

La manera de llamar al método seria la siguiente:

CargarDatosAControles(cliente, typeof(Cliente), panelControl1);

De manera similar se pude implementar el otro método que hace la operación inversa CargarDatosAClases.

Cómo Buscar Controles de ASP .Net con JavaScript Dentro de ContentPages

Ayer como a medio día, uno de mis viejos amigos, (uhmm esa ultima frase no me gusto mucho, me recuerda que estoy cada vez más viejo) me visitó y me comentó que tenia un problema cuando trabajaba con Master Pages y que no lograba que funcione una llamada a un calendario utilizando JavaScript.

Como yo no soy de escapar a estos desafíos, me dispuse a revisar el código y le dije a mi amigo, esto lo soluciono en un par de minutos, luego de media hora de hacer todas las pruebas posibles, tuve que decirle (no tengo la más mínima idea de cómo solucionar esto) que esto necesita ser revisado con paciencia y que lo solucionaría cuando tenga más tiempo.

El problema
Ahora que ya se fue mi amigo, comienzo a depurar con más calma el programa y localizo la fuente del problema, cuando se ejecuta el código desde un content page, la función de JavaScript:

document.getElementById(‘txtFecha’).Value

Devuelve null, es decir no encuentra el control txtFecha que como supondrán es un control TextBox, lo extraño es que cuando se ejecuta el mismo código desde una página Aspx normal que no utiliza master pages, funciona a la perfección.

Al parecer cuando se realiza la combinación del master page con el content page ya no se utilizan los nombres (ID) que les colocamos a los controles de nuestro formulario, esto para evitar posibles conflictos de nombres entre los controles que están en el master y content page.

La solución
Después de buscar información en muchas páginas y de probar infructuosamente muchas posibles soluciones, llegue a la conclusión de que como la función no encontraba el nombre (ID), que le había asignado al control, tendría que buscarlo por otro nombre, y ese nombre es el que esta en la propiedad ClientID de los controles. El valor de ClientID es generado automáticamente concatenando el valor de la propiedad (ID) del control con el valor de la propiedad UniqueID del control padre.

Un pequeño ejemplo
Suponiendo que tenemos un Content Page que se ha creado a partir de un Master Page, y que colocamos dentro un TextBox de nombre: txtFecha y un Button, en la página de código tendremos el método que maneja el evento click del botón:

protected void Button1_Click(object sender, EventArgs e)
{
    string codigo = “document.getElementById(‘”+
        txtFecha.ClientID + “‘).value = ‘Hola'”;
    ClientScript.RegisterStartupScript(GetType(), “prueba”, codigo);
}

Noten que como parámetro de la función document.getElementById utilizamos la propiedad ClientID del TextBox. El fragmento de código anterior solo coloca en la propiedad Text del control txtFecha el valor de “Hola”.

De esta manera solucionamos el problema de los nombres de los controles cuando trabajamos con JavaScript y Master Pages.