miércoles, 28 de enero de 2009

No tan bonito como quería...

Hoy he dedicado la tarde a arreglar este blog.

A conseguir ver el código de forma más legible y que sea menos tostón en meterlo y formatearlo a mano.

Ahora solo falta el diseño, que sigue siendo espantoso...

Ordenar número de tipo String (CHAPUZA)

Ya estamos terminando la pequeña aplicación de inventario de activos en la que llevo metido los dos últimos meses. Y me he encontrado con un problema de compleja resolución que al final he resuelto con una enorme CHAPUZA.

Tengo un campo identificador de nivel que es un string y que es numérico con decimales del 0 al 10. Algo como:

1
2
3
3.4
5
7
10
10.1

El problema es que cuando lo recupero de la base de datos y lo cargo en un grid ordenado por este campo, el 10 se me pone justo detrás del 1, seguido de 10.1.

La primera solución, conviértelo en un número, no funciona porque el IIS está en un equipo en castellano y la BD en un equipo en ingles. O sea, que me tengo que liar a cambiar las Culture y si la combinación no es la que yo quiero, entonces todo casca. Y cómo se acaba la cosa en dos días, no me he molestado en buscar cómo recuperar el idioma del IIS y del SQL.

La segunda, ponerle ceros por delante, me ha provocado empezar a escribir código y no hacer que funcionará del todo bien. Y eso que esta creo que es la solución correcta.

Y volviendo de comer, he dado con una solución que es una CHAPUZA pero funciona muy bien en este caso específico con unta tabla que prácticamente no va a cambiar este campo nunca.

Le he puesto un espacio delante de todos los números a excepción de los que superan la decena. Y al cero le he puesto dos espacios… y yá tá. (jeje, sí que funciona perfectamente en TODO).

viernes, 23 de enero de 2009

Cambios de diseño

Si, está espantoso el Blog. Pero es que estoy intentando utilizar la librería syntaxhighlighter para poder introducir código con alegría y sin tanto lío, y esta plantilla es la única que me funciona bien.

Luego en casa, que ahora estoy trabajando, me curro el modificar una plantilla para que esto funcione bien.

Seleccionar por filas en un GridView

Hay días en que uno se siente "con la musa" y se siente bastante feliz por esos pequeños desarrollos que hacen la vida más fácil.

Mi control preferido, el GridView, me ha dado una nueva alegría al conseguir el siguiente requerimiento del cliente:

Dado un, que se pueda pinchar en una fila y que se abra la ventana de detalle.

En principio esto lo hacíamos con los dos consabidos botones de edición y borrado. Pero el cliente nos pidió si se podía realizar pinchando en la fila en sí.

Me dí un garbeo por San Google y entre un buen número de páginas y técnicas me quede con esta.


Protected Sub GridView_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView.RowDataBound
'' Si es una fila de datos.
If e.Row.RowType = DataControlRowType.DataRow Then
'' Cargo el comportamiento en cada fila.
e.Row.Attributes.Add("onclick", Page.ClientScript.GetPostBackEventReference(sender, "Acción$" & CType(e.Row.FindControl("idcontrol"), Label).Text))
'' Le indico que en utilice el cursor de selección cuando se pase por encima.
e.Row.Style.Add("cursor", "hand")
End If
End Sub



Como ves, el código es simple. En el evento que se lanza cada vez que cargo los datos en una fila del Grid, le añado un evento "onclick" el cual hace un postback que será capturado por el evento RowCommand del propio Grid.

Aquí, las dos cosas que hay que tener cuidadín son las que he resaltado en negrita. Es decir, le tienes que pasar el nombre del comando (CommandName) para poder tener el control de lo que va a ocurrir al pulsar en la fila. (si pones edit o delete o select o etc., tendrá el comportamiento por defecto de cualquier Grid). Y le tienes que indicar (por lógica) cual es el control en el que has cargado el identificador de los datos sobre los que quieres funcionar. En este caso, el identificador en la base de datos de un registro.

Por lo cual en el GridView tendras que añadir o seleccionar la columna que va a proveer el indice de identificación. En este ejemplo, es un Id en una columna oculta y es algo así:


<asp:TemplateField HeaderText="id" Visible="False">
   <ItemTemplate>
      <asp:Label ID="idControl" runat="server" Text='<%# eval("id").toString %>' runat="server" />
   </ItemTemplate>
</asp:TemplateField>


Y ahora me voy a mi evento RowCommand de mi Grid y le añado un condicional para capturar el evento que he añadido en el onlick.


Protected Sub GridView_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GridView.RowCommand
Select Case e.CommandName
Case "Acción"
'' Aquí hace cosas
End Select
End Sub


Jo!! Que bien funciona... peeeeero (siempre hay uno) y aquí viene mi aportación al mundo del desarrollo :), me veo que se superpone el comportamiento de la fila sobre el comportamiento esperado del botón de borrar (el cual no me quiero llevar al detalle). arrggg!!.

Pues, dándole unas poquillas vueltas, me doy cuenta que así como trato sobre la fila entera, puedo hacerlo un poco más granular y aplicar el comportamiento sobre las celdas, dejando fuera las que yo quiera que se comporten de otra forma.

Solución en el siguiente código en donde solamente he cambiado la linea de carga del atributo onclick por un bucle que carga dicho atributo a cada una de las filas excepto la última, que es donde tengo situado el botón de borrado.


Protected Sub GridView_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView.RowDataBound
Dim numCeldas As Integer
'' Si es una fila de datos.
If e.Row.RowType = DataControlRowType.DataRow Then
''Cuento el número de celdas que componen la fila.
numCeldas = e.Row.Cells.Count

'' Cargo el comportamiento en cada celda.
'' En este caso en todas menos en la última que es la de borrado.
For x As Integer = 0 To numCeldas - 2
e.Row.Cells(x).Attributes.Add("onclick", Page.ClientScript.GetPostBackEventReference(sender, "Accion$" & CType(e.Row.FindControl("IdControl"), Label).Text))
Next

'' Le indico que en utilice el cursor de selección cuando se pase por encima.
e.Row.Style.Add("cursor", "hand")
End If
End Sub


Y lo mejor de todo, de todo es que funciona de muerte. Perfecto y de una forma muy poquito vista en la Web.

jueves, 22 de enero de 2009

GridView. Buena práctica para cuando está vacío de datos.

Estoy metido en un pequeño y bonito proyecto en donde he podido hacer, casi, lo que me da la gana incluyendo mi primer proyecto en Scrum con TFS2008, en una máquina virtual, etc. Y, además, con un buen desarrollador a mi lado.

Del diseño gráfico también me estoy encargando yo, multimedia que es uno, y me he encontrado que cuando el SqlAdapter me devuelve los datos vacios, literalmente dejaba de ver el GridView. Lo cual a veces está bien y a veces no.

Y como soy un fan de Google, pues para mis GridView que soportan un buscador le he añadido un mensaje de “No se han encontrado resultados” por defecto en el caso que no haya ninguna fila en el GridView.

¿Y cómo se hace eso?

Te vas al final de tu GridView y justo después de la declaración de las columnas le conformas el template en caso de estar vacío:

   </Columns>
  <EmptyDataTemplate>
     <asp:Label ID="Label21" runat="server" Text="No se ha encontrado ninguna coincidencia con el criterio de búsqueda. />
  </EmptyDataTemplate>
</asp:GridView>


Y si lo incluyes por defecto en las declaraciones de tus GridView… la mayoría de las veces te vas a quitar mucho trabajo y el resultado es mucho mejor que un efecto “ahora te veo, ahora no te veo”.

lunes, 5 de enero de 2009

FckEditor. No se ven las imágenes.

Un pequeño e insidioso error que me hizo dar vueltas durante un buen rato.

Desarrollando una web en .NET 2.0 con VS2008, todo funciona corréctamente con el FckEditor y, sobre todo, con el fileManager incluido.

Cuando, de repente, en pleno desarrollo me encuentro que no me salen las imágenes!!

Empiezo a darle vueltas al asunto y al final la causa/solución, es más bien extraña. Al arrancar el debuging en VS2008, lo tengo configurado para que utilice el Servidor de desarrollo de ASP.NET, el cual utiliza un puerto diferente cada vez, y por lo cual el FckEditor se vuelve tonto y me hace tonterias (como Forrest Gump).

Si lo hago desde localhost, todo va corrécto...

Otro error aún más insidioso, y mucho más peligroso, es que en el webconfig mi cadena de conexión a la base de datos estaba apuntando a... producción!!. Claro, así no encontraba las imágenes nuevas ni visualizaba las imagenes viejas.

FckEditor y el infierno del Upload

El FckEditor es un excelente editor de texto HTML.

Pero también que la configuración por defecto del almacenamiento de archivos da "pequeños" problemas que son incómodos.

El primero, del cual ya he hablado aquí, es un problema de permisos para el gestor de ficheros e/o imagenes. Y trasteando en una aplicación que estoy evolucionando me dí cuenta que en el fichero de configuración del conector que estemos usando (en mi caso aspx) puedo definir en donde voy a almacenar estos ficheros y dentro de qué subdirectorio de acuerdo a la extensión.

En mi caso esta información está en el fichero:
fckeditor\editor\filemanager\connectors\aspxconfig.ascx

Y el Path se definen en el nodo UserFilesPath. el cual por defecto está vacio.

Y aquí viene el problema de permisos que tenemos cuando estamos publicando en un host alquilado (como casi todos). El path por defecto es el RAIZ del Servidor Web.

Y doy un ejemplo real:

Tengo el dominio miDominio.com en un servidor que responde al dominio http://www.miDominio.com el cual está físicamente alojado en:

Raiz del servidor de hosting(comunmente inaccesible)/miDominio/httpDocs/

Al utilizar el fileManager del FckEditor, el sitio por defecto donde intenta almacenar los ficheros es en MiDominio y no en httpDocs, por lo cual lo primero es que dá error de permisos porque la aplicación los tiene para el directorio de publicación web httpDocs y no sobre el directorio miDominio.

Y cuando lo arreglo dando permiso sobre la carpeta raiz miDominio (a mamarla la seguridad si no funciona), entonces no encuentro físicamente donde lo está guardando porque lo busco a partir del directorio de publicación httpDocs cuando en realidad me lo está guardando un nivel por encima... =:0

Osea, que en la configuración lo cambio por:

UserFilesPath = "/httpDocs/";

Y ahora sí que me lo guarda a partir de este punto. Y se acabaron los problemas de permisos.

Pero continuemos con la configuración del fileManager:

Ahora me veo que me está dejando los ficheros en subdirectorios de acuerdo a la extensión del fichero. Por lo cual me voy a la configuración y, como quiero que me guarde las imágenes dentro del subdirectorio imagenes y no en el images, que es el que es por defecto, pues lo modifico:

TypeConfig[ "Image" ].AllowedExtensions = new string[] { "bmp", "gif", "jpeg", "jpg", "png" };
TypeConfig[ "Image" ].DeniedExtensions = new string[] { };
TypeConfig[ "Image" ].FilesPath = "%UserFilesPath%imagenes/";
TypeConfig[ "Image" ].FilesAbsolutePath = ( UserFilesAbsolutePath == "" ? "" : "%UserFilesAbsolutePath%imagenes/" );
TypeConfig[ "Image" ].QuickUploadPath = "%UserFilesPath%";
TypeConfig[ "Image" ].QuickUploadAbsolutePath = ( UserFilesAbsolutePath == "" ? "" : "%UserFilesAbsolutePath%" );

Y ahora si que si. Guardo las cosas en donde me viene mejor. Y sé que le tengo que dar permisos de escritura solamente al directorio imagenes.

Feliz año 2009

Pues eso, hoy día 5 de Enero, deseo un feliz y prospero año nuevo 2009 a todos los que lean estas líneas.

Llevo demasiado tiempo en silencio y los post se me han acumulado: TFS, TFS Scrum, Ajax, etc, etc.

A ver si me pongo al día, que estoy desarrollando un proyectito interesante y con un buen compañero de equipo.