27.10.09

Cómo invocar un archivo CSS incrustado en ASP.NET

Recientemente me vi en la necesidad de crear un componente a medida (control web). Se trataba de un TextBox que pudiese controlar distintos tipos de datos (números, fechas, textos con limitaciones, etc). Ninguno de los freeware me convencía, por lo que puse manos a la obra y lo comencé a escribir desde mi poco conocimiento actual sobre el tema.

El objeto en cuestión se encuentra en una .dll, junto con tres archivos Javascript y un archivo CSS. Hasta ahí todo fenómeno. Los archivos .js se deben incluir en la página dependiendo del tipo de validación que se necesite y el .css cuando el control necesite mostrar un calendario para que el usuario ingrese una fecha de forma más cómoda.

La idea es que los archivos incrustado en la biblioteca de controles se invoquen mediante el etiquetado normal (<script> y <link>) y no volcar el código directamente en la página, puesto que en los sucesivos postback el servidor se vería obligado a transmitir todo ese código.

Como dijera, mi escaso conocimiento de la construcción de controles me llevó a leer toda la documentación posible y buscar en la web los ejemplos más claros. La mayoría de los ejemplos para invocar los .js decían más o menos así:

protected override void OnPreRender(EventArgs e) {
Page.ClientScript.RegisterClientScriptInclude("FunkyJavaScript",
Page.ClientScript.GetWebResourceUrl(this.GetType(),
  "FunkyTextBox.Funky.js"));

base.OnPreRender(e);
}
Esto funciona correctamente para los .js, especialmente cuando se colocan varios controles del mismo tipo. Para el caso de los .css el código aconsejado en varios ejemplos es este:
string includeTemplate =
"<link rel='stylesheet' text='text/css' href='{0}' />";
string includeLocation =
Page.ClientScript.GetWebResourceUrl(this.GetType(), "myStylesheet _Links.css");
LiteralControl include =
new LiteralControl(String.Format(includeTemplate, includeLocation));
((HtmlControls.HtmlHead) Page.Header).Controls.Add(include);

Si leemos el código, lo que hace es agregar una etiqueta LINK al encabezado de la página enlazando el archivo incrustado. Lo problemático de esto es que si agregas tres controles, este código agregará tres etiquetas LINK. Y eso no es lo que queremos!!

Bueno... toda esta cháchara para explicarles con que bobada lo resolví y tal vez le sirva. También puede pasar que haya una mejor forma y que yo quede como un idiota ignorante. :-)

If Page.Header.FindControl("mcTextBoxCalendarCSS") Is Nothing Then
Dim lnkCss As New HtmlControls.HtmlLink
With lnkCss
.Attributes("rel") = "stylesheet"
.Attributes("text") = "text/css"
.ID = "mcTextBoxCalendarCSS"
.Href = Page.ClientScript.GetWebResourceUrl(Me.GetType(), "mcTextBoxCalendar.css")
 End With
Page.Header.Controls.Add(lnkCss)
End If

Lo que hace este código es simple, crea el mismo enlace, pero le pone un ID y si ya existe de un objeto anterior, no lo agrega. Simple, pero efectivo.

24.1.09

RadiusIM

He encontrado un navegador alternativo muy interesante que tiene funcionalidaes parecidas al desaparecido Odigo que mucho internaultas extrañan: radiusIM.

Tal vez ustedes ya lo conozcan y yo llegue tarde con la noticia, pero la finalidad de esta entrada en mi blog es para que mucha más gente se registre en él. Hay que hacerlo crecer porque, a diferencia de los otros mensajeros, con este puedes encontrar montones de amigos.

La característica más interesante es que situa a tus probables amigos en el mapa de Google. Puedes señalar tu ubicación en el mundo y radiusIM buscará personas afines a ti que se encuentren cerca de tu ubicación. O puesdes desplazarte por el Globo y radiusIM te mostrará quienes se encuentran registrados cerca de esa posición.

Tambiém puedes comunicarte con los navegadores más populares, como no podía ser de otra manera. Puedes invitar a tus amigos de esos mismos mensajeros a que se una.

Si tu intención es que las personas que te contacten tengan tu permiso para hacerlo, pues solo tienes que establecerlo en tu configuración y listo.

Su buscador no tiene tantas opciones como el querido Odigo, pero sería cuestión de que lo sugiriesemos y ver si lo pueden implementar.

A mi me agrada y se los recomiendo.

28.8.08

Objeto Javascript para controlar objetos INPUT

Trabajamos a diario, los que diseñamos páginas web, con formularios de entrada de datos, mayormente conformados por objetos INPUT. Estos requieren, muchas veces, de validaciones estándares que, si bien podemos hacerlas en el servidor, se ahorra mucho tiempo el hacerlas en el servidor.

Este objeto que les comparto, hace una serie de validaciones para distintos tipos de entradas de datos, algunas de ellas teniendo en cuenta a algunos caracteres "indigestos" para las bases de datos enalgunas circunstancias. Valida:

  • NUMBER: que la entrada sea un valor numérico, entero o decimal, positivo o negativo, con determinada cantidad de decimales, etc.
  • DATE: que la entrada sea una fecha válida. Solo acepta fechas en formato dd/mm/aaaa.
  • TEXTONLY: solo caracteres de la A-Z (sin los hispánicos, salvo que se establezca lo contrario).
  • TEXTNUMBER: caracteres de la A-Z, números de 0-9 y el espacio en blanco. (sin los hispánicos, salvo que se establezca lo contrario).
  • ALPHA: caracteres de la A-Z y algunos símbolos. (sin los hispánicos, salvo que se establezca lo contrario).
  • ALPHANUMERIC: caracteres de la A-Z, números de 0-9 y algunos símbolos. (sin los hispánicos, salvo que se establezca lo contrario).

El código:

var __jgWC = {
   Version: '0.2.0 RC',
   //objetos controlados
   ControledObjects: [],
   //determinador del browser
   Browser: {
       IE:     !!(window.attachEvent && !window.opera),
       Opera:  !!window.opera,
       WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
       Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
       MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
   },
   //controla los atributos del objeto input
   DefaultAttributes:function(obj) {
       if(!obj.getAttribute('inputtype')) obj.setAttribute('inputtype', 'all');
       if(!obj.getAttribute('texttransform')) obj.setAttribute('texttransform', 'none');
       if(!obj.getAttribute('positiveonly')) obj.setAttribute('positiveonly', 'false');
       if(!obj.getAttribute('decimalpoint')) obj.setAttribute('decimalpoint', '.');
       if(!obj.getAttribute('decimalplaces')) obj.setAttribute('decimalplaces', '2')
       if(!obj.getAttribute('excludechar')) obj.setAttribute('excludechar', '');
       if(!obj.getAttribute('includecharonly')) obj.setAttribute('includecharonly', '');
       if(!obj.getAttribute('spanishchars')) obj.setAttribute('spanishchars', 'false');
       if(!obj.getAttribute('valMin')) obj.setAttribute('valMin', '');
       if(!obj.getAttribute('valMax')) obj.setAttribute('valMax', '');
       if(!obj.getAttribute('blurErrorAlert')) obj.setAttribute('blurErrorAlert', 'true');
       if(!obj.getAttribute('blurErrorMessage')) obj.setAttribute('blurErrorMessage', 'DATOS INCORRECTOS');
   },
   //registrador de los eventos de los inputs controlados
   RegisterObject:function(ob, enableEvents) {
       if(typeof ob == 'string') { ob=document.getElementById(ob); }
       if(arguments.length == 1) {enableEvents=true;}
       if(!ob) { return false;}
       this.ControledObjects.push(ob);
       this.DefaultAttributes(ob);
       if(enableEvents) {
           this.AttachEvent(ob, 'keypress', __jgWC_DoKeyPress, false);
           this.AttachEvent(ob, 'blur', __jgWC_DoBlur, false);
       }
       return true;
   },

   //agregador de los eventos
   AttachEvent:function(elemento, nombre_evento, funcion, captura) {
     if (elemento.attachEvent){
       elemento.attachEvent('on' + nombre_evento, funcion);
       return true;
     }else
       if (elemento.addEventListener){
         elemento.addEventListener(nombre_evento, funcion, captura);
         return true;
       }else {
         return false;
       }
   },

   //cancela la ultima entrada
   CancelInput:function(ev) {
       if(this.Browser.IE)
       {
           ev.keyCode = 0;
           ev.returnValue = 0;
       }else{
           ev.preventDefault();
           ev.stopPropagation();
       }
   },

   //detiene el burbujeo del evento
   StopBubble:function(ev) {
       if (__jgWC.Browser.IE) {
           ev.cancelBubble = true;
       }else{
           ev.stopPropagation();
       }
   },

   //evaluador de los dígitos ingresados
   EvaluateValue:function(obj, kcASCII, eventType)
   {
       kcASCII = (arguments.length==1) ?'':kcASCII;
       var value = obj.value + kcASCII;
       // excludechar
       var exChar = obj.getAttribute('excludechar');
       if(exChar.length > 0) {
           return ( RegExp('^[^' + exChar + ']*$', 'i').test(value));
       }
       // includeonly
       var inOnly = obj.getAttribute('includecharonly');
       if(inOnly.length > 0) {
           return (RegExp('^[' + inOnly + ']*$', 'i').test(value));
       }
       // todo tipo de caracteres
       if(obj.getAttribute('INPUTTYPE').toUpperCase() == 'ALL') { return true; }

       switch(obj.getAttribute('INPUTTYPE').toUpperCase()) {
           // tipo numerico
           case 'NUMBER':
               return this.NumericValue(obj, value);
               break;
           //tipo fecha
           case 'DATE':
               if(eventType == 'keypress') { return true; }
               return this.DateValue(obj, value);
               break;
           //solo texto
           case 'TEXTONLY':
               return this.TextOnlyValue(obj, value);
               break;
           //solo texto y numeros
           case 'TEXTNUMBER':
               return this.TextNumberValue(obj, value);
               break;
           //texto y algunos caracteres de puntuacion
           case 'ALPHA':
               return this.AlphaValue(obj, value);
               break;
           //texto, numeros y algunos caracteres de puntuacion
           case 'ALPHANUMERIC':
               return this.AlphaNumericValue(obj, value);
               break;
       }
       return true;
   },

   //muestra el mensaje de error establecido si se seteo el permiso para mostrarlo
   ErrorMessage:function(obj) {
       if(obj.getAttribute('blurErrorAlert') == 'true') {
           alert(obj.getAttribute('blurErrorMessage'));
           obj.select();
           obj.focus();
       }
   }
};

//agrega caracteres hispanicos a la expresion regular
__jgWC.addSpanishChars=function(obj, alpha) {
       return (obj.getAttribute('SPANISHCHARS').toLowerCase()=='true')?'áéíóúüñ' + ((alpha)?'¿¡':'') :'';
   }

//agrega simbolos a la expresion regular    
__jgWC.addSimbols=function(obj) {
       return ':\\?\\!\\$%=/*#<>\\+->,;\\(\\)\\.@_';
   }

//testea el valor ALPHANUMERIC
__jgWC.AlphaNumericValue=function(obj, valToTest) {
       var Exp = '^[a-z0-9\s' + this.addSpanishChars(obj, true) + this.addSimbols(obj) + ']*$';
       return RegExp(Exp).test(valToTest.toLowerCase());
   }

//testea el valor ALPHA
__jgWC.AlphaValue=function(obj, valToTest) {
       var Exp = '^[a-z\s' + this.addSpanishChars(obj, true) + this.addSimbols(obj) + ']*$';
       return RegExp(Exp).test(valToTest.toLowerCase());
   }

//testea el valor TEXTNUMBER
__jgWC.TextNumberValue=function(obj, valToTest) {
       var Exp = '^[a-z0-9\s' + this.addSpanishChars(obj, false) + ']*$';
       return RegExp(Exp).test(valToTest.toLowerCase());
   }

//testea el valor TEXTONLY
__jgWC.TextOnlyValue=function(obj, valToTest) {
       var Exp = '^[a-z\s' + this.addSpanishChars(obj) + ']*$';
       return RegExp(Exp).test(valToTest.toLowerCase());
   }

//testea el valor NUMERIC
__jgWC.NumericValue=function(obj, valToTest) {
       var Exp = '^';
       if(obj.getAttribute('positiveonly').toLowerCase()=='false') {
           Exp += '[-]?';
       }
       Exp += '\\d{1,1}\\d*';
       if(Number(obj.getAttribute('decimalplaces')) > 0) {
           Exp += '\\' + obj.getAttribute('decimalpoint') + '?\\d{0,' + obj.getAttribute('decimalplaces') + '}';
       }
       Exp += '$'; 
       return RegExp(Exp).test(valToTest);
   }

//transforma el texto segun el atributo TEXTTRANSFORM
__jgWC.TextTransform=function(obj) {
       switch(obj.getAttribute('TEXTTRANSFORM').toUpperCase()) {
           case "UPPER":
               obj.value = obj.value.toUpperCase();
               break;
           case "LOWER":
               obj.value = obj.value.toLowerCase();
               break;
       }
   }
 

//controla el valor DATE
__jgWC.DateValue=function(obj, valToTest) {
       var expr='^(([0-2]?\\d{1})|([3][0,1]{1}))\/[0,1]?\\d{1}[0-2]\/[1-9]{1}\\d{3}$';
       if( ! RegExp(expr).test(valToTest) ) { return false;};
       var aD = valToTest.split('/');
       var tmp = new Date(aD[2], aD[1]-1, aD[0]);
       if(Number(tmp.getDate()) != Number(aD[0]) || Number(tmp.getMonth()+1) != Number(aD[1]) ||
               Number(tmp.getFullYear()) != Number(aD[2]) ) {
           return false;
       }
       return true;
   }


//controla los objetos indicados en ControledObjects uno por uno
function __jgWC_ControlObjects() {
   var objs = __jgWC.ControledObjects;
   var len = objs.length;
   for(var i=0; i < len; i++) {
       var obj = objs[i];
       if( !__jgWC.EvaluateValue(obj) ) {
           __jgWC.ErrorMessage(obj);
           return false;
       }
   }
   return true;
}


//controla el evento KeyPress
function __jgWC_DoKeyPress(ev) {
   var obj = ev.target||ev.srcElement;
   var KC = ev.which||ev.keyCode;
   var kcASCII = String.fromCharCode(KC);
   var noChar = ev.keyCode;
   __jgWC.StopBubble(ev);
   if(__jgWC.Browser.IE) {
       if (document.selection.type == 'Text')    {
           var tr = document.selection.createRange();
           tr.text = '';
       }
   }else {
       if(obj.selectionStart < obj.selectionEnd) {
           if(String(noChar).search('37|39') == -1) {
               try {
                   window.getSelection().collapseToStart()
               }catch(ex){ }
           }
       }
   }
   if( ev.ctrlKey || ev.altKey ) {
       if(kcASCII.search('V|v|C|c|X|x') > -1) {
           return true;
       }
       __jgWC.CancelInput(ev);
       return false;
   }

   // backspace|tab|enter|Alt-flecha izq|Alt fecha der.
   if(String(noChar).search('8|9|13|37|39') > -1) {return true;}
   //evaluate
   if( !__jgWC.EvaluateValue(obj, kcASCII, ev.type) ) {
       __jgWC.CancelInput(ev);
       return false;
   }
}


//controla el evento blur
function __jgWC_DoBlur(ev) {
   var obj = ev.target||ev.srcElement;
   //evaluate
   if( !__jgWC.EvaluateValue(obj) ) {
       __jgWC.ErrorMessage(obj);
       return false;
   }
   __jgWC.TextTransform(obj);
   return true;
}

Las propiedades que acepta cada INPUT son:

  • INPUTTYPE: indica que tipo de dato hay que controlar: NUMERIC, DATE, TEXTONLY, TEXTNUMBER, ALPHA, ALPHANUMERIC, ALL.
  • TEXTTRANSFORM: acepta los valores UPPER, LOWER o NONE, para indicar si se debe transformar el texto a mayúsculas, minúsculas o sin cambios, respectivamente.
  • POSITIVEONLY: funciona en combinación con NUMERIC y acepta el valor TRUE o FALSE. Indica si el valor númerico será solo positivo o no.
  • DECIMALPOINT: funciona en combinación con NUMERIC, para establecer el punto decimal que se ha de aceptar. Por omisión se acepta el punto.
  • DECIMALPLACES: funciona en combinación con NUMERIC y determina el número de decimales que se haceptarán. Por omisión se aceptan 2.
  • EXCLUDECHAR: acepta una lista de caracteres que no serán aceptados en la entrada de datos.
  • INCLUDECHARONLY: acepta una lista de caracteres que serán los únicos aceptados.
  • SPANISHCHARS: acepta el valor TRUE o FALSE e indica si se han de aceptar los caracters españoles áéíóúñ en la entrada de datos.
  • VALMIN: funciona en combinación con NUMERIC y establece el valor mínimo que se ha de aceptar.

27.8.08

Objeto Javascript para manipular ComboBox

Hoy les acerco un objeto Javascript que permite manipular ComboBox de manera un tanto más sencilla y práctica

Entre sus propiedades, éste permite:

  • Crear el combo a través de un par de métodos y con algunas propiedades, de manera dinámica.
  • Obtener el valor de la opción seleccionada, sus atributos y/o el texto de la opción.
  • Eliminar todas o algunas de las opciones.
  • Ordenar las opcines por su valor o por su texto identificatorio.
  • Seleccionar la opción del combo, basándose en el valor o el texto de la misma.

El código:


/** Documento Javascript
Funciones para tratar objetos SELECT

Autor: Julio Gonzalez
Fecha: 22/08/2008
*/

$CB = {
/* metodo interno */
__ctrlOBJ:function(ob) {
 if( typeof ob != 'object' ) { return false; }
 if( ! ob ) { return false; }
 return true;
},

/* metodo interno */
__isArray:function(ob) {
 return (ob != null && typeof ob == "object" && 'splice' in ob && 'join' in ob);
},

/* metodo interno */
__isObject:function(ob) {
return (ob != null && typeof ob == "object");
},

/* metodo interno */
__getIndexByValue:function(ob, val, sensitive) {
 if(!this.__ctrlOBJ(ob)) { return null; }
 if(arguments.length < 3) { sensitive = false; }
 var ve;
 val = (sensitive)?val:val.toLowerCase();
 for(var i=0, len=ob.length; i < len; i++)
 {
     ve = (sensitive)?ob.options[i].value:ob.options[i].value.toLowerCase();
     if( ve == val ) {    return i; }
 }
 return null;
},

/* metodo interno */
/* seleccionar una opcion por el texto de la opcion */
__getIndexByText:function(ob, txt, sensitive) {
 if(!this.__ctrlOBJ(ob)) { return null; }
 if(arguments.length < 3) { sensitive = false; }
 var ve;
 txt = (sensitive)?txt:txt.toLowerCase();
 for(var i=0, len=ob.length; i < len; i++)
 {
     ve = (sensitive)?ob.options[i].text:ob.options[i].text.toLowerCase();
     if( ve == txt ) {    return i; }
 }
 return null;
},

/* elimina todos los elementos de un combo */
Clear:function(ob) {
 if(!this.__ctrlOBJ(ob))  { return false; }
 for(var i = ob.length - 1; i >= 0; i--)
 { ob.remove(i);  }
 return true;
},

/* borra una opcion buscandola por su value 
        sensitive (valor booleano) indica si la busqueda sera sensible a mayusculas/minusculas */
DeleteOptionByValue:function(ob, val, sensitive) {
 var i = this.__getIndexByValue(ob, val, sensitive);
 if(i==null) { return false; }
 ob.remove(i);
 return true;
},


/* borra una opcion buscandola por su value 
        sensitive (valor booleano) indica si la busqueda sera sensible a mayusculas/minusculas  */
 DeleteOptionByText:function(ob, txt, sensitive) {
 var i = this.__getIndexByText(ob, txt, sensitive);
 if(i==null) { return false; }
 ob.remove(i);
 return true;
},

/* retorna el texto de la opcion seleccionada */
TextSelectedOption:function(ob) {
 if(!this.__ctrlOBJ(ob) || !this.IsSelected(ob)) { return null; }
 return ob.options[ob.selectedIndex].text;
},

/* retorna el valor de la opcion seleccionada */
ValueSelectedOption:function(ob)   {
 if(!this.__ctrlOBJ(ob) || !this.IsSelected(ob)) { return null; }
 return ob.options[ob.selectedIndex].value;
},


/* retorna el objeto de la opcion seleccionada */
ObjectSelectedOption:function(ob) {
 if(!this.__ctrlOBJ(ob) || !this.IsSelected(ob)) { return null; }
 return ob.options[ob.selectedIndex];
},

/* retorna verdadero si el selectedIndex es > -1 */
IsSelected:function(ob)    {
 if(!this.__ctrlOBJ(ob)) { return false; }
 return (ob.selectedIndex > -1);
},


/* seleccionar una opcion por la propiedad value de la opcion 
        sensitive (valor booleano) indica si la busqueda sera sensible a mayusculas/minusculas  */
SelectByValue:function(ob, val, sensitive)
{
 var i = this.__getIndexByValue(ob, val, sensitive);
 if(i==null) { return false; }
 ob.selectedIndex = i;
 return true;
},


/* seleccionar una opcion por el texto de la opcion 
        sensitive (valor booleano) indica si la busqueda sera sensible a mayusculas/minusculas  */
SelectByText:function(ob, txt, sensitive)
{
 var i = this.__getIndexByText(ob, txt, sensitive);
 if(i==null) { return false; }
 ob.selectedIndex = i;
 return true;
},


/* objeto para crear el objeto necesario en CreateHTMLCombo */
ComboObject:{
 __cb:{id:'', name:'', attr:[], options:[]},
 setID:function(v) { this.__cb.id=v; },
 setName:function(v) { this.__cb.name=v; },
 addSelectAttribute:function(v) { this.__cb.attr.push(v); },
 addOption:function(txt, aAtt) {
     var ob={};
     ob.text=txt;
     ob.attr=aAtt;
     this.__cb.options.push(ob);
 },
 getComboObject:function() { return this.__cb; }
},


/* Crea el codigo html de un combo a partir de un obejto con la definicion de la etiqueta,
        y un array de objeto con el contenido de las etiquetas option.
        Datos del select
        - attr  array de pares (attr=value) con atributos del objeto (incluyendo los del usuario)
        - id        id del objeto select (obligatorio)
        - name  name dentro del <form>
   

        Datos de los options (Array)
        - text    texto de cada option
        - attr  array de pares (attr=value) con atributos del objeto (incluyendo los del usuario)
    */
CreateHTMLCombo:function(oSelDef) {
 if( !this.__isObject(oSelDef) ) { return null; }
 var aOp = oSelDef.options;
 if( !this.__isArray(aOp) ) {  return null; }
 /* define el select */
 if(typeof oSelDef.id != 'string') { return null; }
 var id = oSelDef.id.replace(/^\s+|\s+$/g,"");
 if(typeof oSelDef.name != 'string' || oSelDef.name.length==0) { oSelDef.name = id; }
 var htm = ['<select id="' + id + '" name="' + oSelDef.name + '"'];
 var aAttr = oSelDef.attr;
 if( this.__isArray(aAttr) ) {
     for(var i=0, len = aAttr.length; i < len; i++)
     {
         var tmp = aAttr[i].split('=');
         if(tmp.length > 1) {
             htm.push(' ' + tmp[0] + '="' + tmp[1] + '"');
         }
     }
 }
 htm.push('>');
     /* agrega los options */
 for(var i=0, len = aOp.length; i < len; i++)
 {
     var op = aOp[i];
     htm.push('<option');
     var aAttr = op.attr;
     if( this.__isArray(aAttr) ) {
         for(var e=0, len2 = aAttr.length; e < len2; e++)
         {
             var tmp = aAttr[e].split('=');
             if(tmp.length > 1) {
                 htm.push(' ' + tmp[0] + '="' + tmp[1] + '"');
             }
         }
         htm.push('>' + ((op.text)?op.text:'') + '</option>');
     }
 }
 htm.push('</select>');
 return htm.join('');
},

/* funcion interna */
/* acomoda un combo de acuerdo con el texto o el valor de sus options 
        sensitive (valor booleano) indica si la busqueda sera sensible a mayusculas/minusculas  */
__SortBy:function(ob, by, sensitive) {
 if(!this.__ctrlOBJ(ob) || !this.IsSelected(ob)) { return false; }
 if(!sensitive) { sensitive=false; }
 var b = new Array();
 var c = ob.getElementsByTagName('option');
 for ( var i in c )
 {
     try {
         b.push( { text:((by=='T')?c[i].text:c[i].value),
                      node:ob[i].cloneNode(true),
                      selected:ob[i].selected} );
     } catch(e) { continue; }
 }
 if(sensitive)
 {
     b.sort(function(opt1, opt2) {
         return opt1.text < opt2.text ? -1 : opt1.text > opt2.text ? 1 : 0;
     });
 }else{
     b.sort(function(opt1, opt2) {
         return opt1.text.toLowerCase() < opt2.text.toLowerCase() ? -1 :
                         opt1.text.toLowerCase() > opt2.text.toLowerCase() ? 1 : 0;
     });
 }
 this.Clear(ob);
 for ( var i in c )
 {
     try {
         var d = b.shift();
         ob.appendChild(d.node);
         if(d.selected) { ob.options[ob.options.length-1].selected=true;}
     } catch(e) { continue; }
 }
 return true;
},


/* acomoda un combo de acuerdo con el texto de sus options 
        sensitive (valor booleano) indica si la busqueda sera sensible a mayusculas/minusculas  */
SortByText:function(ob, sensitive) {
 return this.__SortBy(ob, 'T', sensitive);
},

/* acomoda un combo de acuerdo con el valor de sus options 
        sensitive (valor booleano) indica si la busqueda sera sensible a mayusculas/minusculas  */
SortByValue:function(ob, sensitive) {
 return this.__SortBy(ob, 'V', sensitive);
}
}

Su uso es bien sencillo:


<div id='j'></div>


<div><button type="button" onClick="GenCombo()">Generar Combo</button></div>
<div><button type="button" onClick="sortCB('T')">Sort por text</button></div>
<div><button type="button" onClick="sortCB('V')">Sort por value</button></div>

<script language="javascript">
/* creacion del combo mediante javascript */
cb = $CB.ComboObject;
//id del select
cb.setID('myCbo');
//atributos de la etiqueta <select>
cb.addSelectAttribute('class=prueba nodes')
cb.addSelectAttribute('size=5')
//definicion de los options
cb.addOption('W 1', ['value=33','selected=true', 'style=background-color:red;'])
cb.addOption('a 1', ['value=11'])
cb.addOption('e 2', ['value=123'])

//ejemplo de creacion del combo
function GenCombo() {
    var j = $CB.CreateHTMLCombo(cb.getComboObject())
    cbo = document.getElementById('j')
    cbo.innerHTML = j
    document.getElementById('myCbo').onchange=mostrar;
}

//ejemplo de ordenacion de combo
function sortCB(val) {
    alert(cbo.innerHTML)
    switch(val) {
        case 'T':
            $CB.SortByText(document.getElementById('myCbo'), true);
            break;
        case 'V':
            $CB.SortByValue(document.getElementById('myCbo'), true);
            break;
    }
    alert(cbo.innerHTML)
}

function mostrar()
{
    var ob = $CB.ObjectSelectedOption(this)
    alert(ob.innerHTML)
}

</script>

Pueden crear el combo mediante el uso de este objeto o pueden manipular uno existente en la página.

Personalmente, cuando se trata de combos dinámicos, cargo la página sin el combo y luego, mediante AJAX, busco el contenido del mismo y genero el combo dentro de un contenedor existente en la página, generalmente un SPAN.

18.8.08

Serializar un DataSet a JSON

Recientemente me encontre con la necesidad en ASP.NET de serializar un DataSet para enviarlo, via AJAX, a un browser, para luego allí procesarlo convenientemente.

Un buen compañero de trabajo, Leonardo Villalva, es el autor de un código en el viejo ASP, para serializar ADO Recordset a formato JSON. Basándome en ese código arme esta clase que permite hacer la misma tarea en .NET, y aquí la publico para compartirla con ustedes.




' ------------------------------------------------------------------
' Clase   : JSONSerializer
' Autor   : Julio González
' Fecha   : 14/07/2008
' ------------------------------------------------------------------
Imports Microsoft.VisualBasic
Imports System.Data


Namespace JG
   Public Class JSONSerializer
      Public Function DataTableSerializer(ByVal dt As DataTable) As String
         Dim JsonString As New StringBuilder()
         Dim tmpR, tmpA As StringBuilder

         'el objeto no existe o no tiene filas
         If dt Is Nothing Or dt.Rows.Count = 0 Then
            Return String.Empty
         End If

         'serializo
         JsonString.Append("{")
         JsonString.Append("filas:[")
         tmpA = New StringBuilder
         For Each f As DataRow In dt.Rows
            tmpA.Append("{")
            tmpR = New StringBuilder
            For Each c As DataColumn In dt.Columns
               tmpR.Append(c.ColumnName & ":""" & f(c.ColumnName).ToString & """,")
            Next
            tmpA.Append((tmpR.ToString.Remove((tmpR.ToString.Length - 1), 1)))
            tmpA.Append("},")
         Next
         JsonString.Append(tmpA.ToString.Remove((tmpA.ToString.Length - 1), 1))
         JsonString.Append("]}")

         Return JsonString.ToString
      End Function
   End Class

End Namespace


Espero que les sea útil.