miércoles, octubre 26, 2011

Herramientas para desarrollo y diseño web

A continuación dejo un listado de algunas herramientas utiles para el momento de diseñar y desarrollar una aplicación web

Herramientas para desarrolladores web:

browser shots: Util herramienta web para revisar la compatibilidad de los navegadores con tu aplicación web, solo debes introducir la url y elegir en que navegadores deseas probar la compatibilidad, puedes encontrar desde versiones antiguas de navegadores hasta sus versiones mas actuales. la pagina de browser shots es
http://browsershots.org/

IE Tester: Programa gratuito que permite medir la compatibilidad de tus aplicaciones web con las versiones mas antiguas de internet explorer. Puedes descargarlo en la siguiente pagina
http://www.my-debugbar.com/wiki/IETester/HomePage

Firebug: Complemento para Mozila que permite hacer seguimiento a cualquier pagina web, obteniendo asi las imagenes que usa, css, html, javascript, etc. Es una herramienta muy potente, para poder usarlo debes tener instalado mozila e instalar el complemento.

Validar CSS: Util herramienta web que te permite validar CSS de acuerdo a los estandares, para poder usarlo debes ingresar la ruta del archivo css y elegir la versión de css con la cual deseas validar.
http://jigsaw.w3.org/css-validator/#validate_by_uri+with_options


CSS Sprite Generator: Herramienta web que permite unir un conjunto de imagenes en una unica imagen, muy util al momento de crear spriters. http://spritegen.website-performance.org/



Herramientas para diseñadores web

Pixel Ruler: Util regla que te permite medir en pixeles cualquier objeto que se encuentre en tu ordenador. Es un software gratuito y lo puedes descargar en http://www.mioplanet.com/products/pixelruler/

ColorPick: Permite obtener el color de cualquier objetos dentro del ordenador en 3 formatos diferentes

martes, octubre 25, 2011

Como saber que versiones del Framework .Net estan instalados

En el portal de Codeplex pueden encontrar un util programa que al ejecutar, automaticamente muestra todas las versiones instalados en el ordenador del .Net Framework.





puedes descargarlo de la siguiente dirección http://frameworkdetector.codeplex.com/


Alternativa libre a MS Project

Buscando por la web encontre un programa para gestionar tareas, actividades, similar a Microsoft Project, me refiero a Gant Project.

Gant Project es una alternativa libre al ya conocido MS Project, puedes descargarlo de la siguiente pagina http://www.ganttproject.biz/

sábado, octubre 22, 2011

Saber el PRODUCTCODE de un instalador usando Batch

despues de algunos meses sin postear escribire algo acerca del PRODUCTOCODE de los paquetes de instalación de windows.



Cuando generamos un instalador de un proyecto hecho en .NET, se genera un paquete con la extension msi. Al generar un instalador podemos indicarle la versión, la ruta donde se intalará, el ProductCode entre otras cosas.



Cada vez que se cambia la version en la capa de instalación de un proyecto en .NET, el entorno hace la siguiente pregunta:






Si le damos click en Sí, el ProductCode cambiará automaticamente.


el productCode de una aplicación .NET es importante a la hora de querer desinstalar una versión antigua o si derrepente se quiere realizar una actualizacion a la versión actual.


Teniendo en claro esto, que pasa si solo se cuenta con el instalador .msi y no se tiene las fuentes o si por ahi se realizó una instalacion con cierta versión y las fuentes originales sufrieron mas cambios de versiones sin haber guardado un historial.


Una solución para saber el ProductCode de un instalador es la siguiente:

Crear el archivo GetMSIProductCode.vbs (la extensión debe ser .vbs) y colocar el siguiente script:





'---------------------------------------------------------------------------------------------


' GetMSIProductCode.vbs

Option Explicit

' Variables
Const msiOpenDatabaseModeReadOnly = 0

' Get command-line arguements
Dim argCount:argCount = Wscript.Arguments.Count

' Connect to the Windows Installer object.
On Error Resume Next
Dim installer : Set installer = Nothing
Set installer = Wscript.CreateObject("WindowsInstaller.Installer") : CheckError

' Open the database (read-only).
Dim databasePath:databasePath = Wscript.Arguments(0)
Dim openMode : openMode = msiOpenDatabaseModeReadOnly
Dim database : Set database = installer.OpenDatabase(databasePath, openMode) : CheckError

' Extract language info and compose report message
Wscript.Echo "Database (MSI) = " & databasePath
Wscript.Echo "ProductName = " & ProductName(database)
Wscript.Echo "ProductCode = " & ProductCode(database)

' Clean up
Set database = nothing
Wscript.Quit 0

' Get the Property.ProductName value.
Function ProductName(database)
On Error Resume Next
Dim view : Set view = database.OpenView("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductName'")
view.Execute : CheckError
Dim record : Set record = view.Fetch : CheckError
If record Is Nothing Then ProductName = "Not specified!" Else ProductName = record.StringData(1)
End Function

' Get the Property.ProductCode value.
Function ProductCode(database)
On Error Resume Next
Dim view : Set view = database.OpenView("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductCode'")
view.Execute : CheckError
Dim record : Set record = view.Fetch : CheckError
If record Is Nothing Then ProductCode = "Not specified!" Else ProductCode = record.StringData(1)
End Function

Sub CheckError
Dim message, errRec
If Err = 0 Then Exit Sub
message = Err.Source & " " & Hex(Err) & ": " & Err.Description
If Not installer Is Nothing Then
Set errRec = installer.LastErrorRecord
If Not errRec Is Nothing Then message = message & vbNewLine & errRec.FormatText
End If
Fail message
End Sub

Sub Fail(message)
Wscript.Echo message
Wscript.Quit 2
End Sub


'---------------------------------------------------------------------------------------------


En el disco “C:\” copiar el instalador .msi del cual se desea saber su ProductCode y además copiamos el archivo GetMSIProductCode.vbs que acabamos de crear

En la ventana de ejecutar colocamos CMD e ingresamos al DOS

Luego, en la ventana de DOS nos situamos en el disco “C:\“ (o en el directorio que elijas pero debes copiar el instalador y el GetMSIProductCode.vbs)

Escribimos el siguiente código:

cscript GetMSIProductCode.vbs "[nombre del instalador].msi"

si el instalador se encuentra en otra ruta debes colocar la dirección completa.

El resultado arrojará el ProductCode en el DOS.

viernes, abril 29, 2011

UserControl: NotificationBar

El siguiente control funciona como un mensaje dentro del formulario que lo contenga.

Para poder crear este control debemos agregar un nuevo UserControl con el nombre NotificationBar.vb y escribir el siguiente código:
Public Class NotificationBar

Private _NumberOfTimesBlink As Integer

Public Enum NotificationIconType
Exclamation
Information
Question
End Enum

_
Public Property BlinkTimes() As Integer
Get
Return Me._NumberOfTimesBlink
End Get
Set(ByVal value As Integer)
Me._NumberOfTimesBlink = value
If value Then
Me.TableLayoutPanel1.ColumnStyles(0).Width = 35
Else
Me.TableLayoutPanel1.ColumnStyles(0).Width = 0
End If
End Set
End Property

Public Sub New()
InitializeComponent()
Me.Visible = False
Me.Dock = DockStyle.Top
Me._NumberOfTimesBlink = 7
End Sub

Public Sub Clear()
lblMensaje.Text = ""
Me.picIcon.Image = Nothing
Me.Visible = False
End Sub
Public Sub ShowNotificiation(ByVal NotificationText As String, ByVal NotificationType As NotificationIconType, ByVal Blink As Boolean)
Me.Visible = True
Clear()
Me.lblMensaje.Text = NotificationText
Select Case NotificationType
Case NotificationIconType.Exclamation
Me.picIcon.Image = My.Resources.Resources.WarningHS
Case NotificationIconType.Information
Me.picIcon.Image = My.Resources.Resources.important
Case NotificationIconType.Question
Me.picIcon.Image = My.Resources.Resources.question16
End Select
Me.Visible = True
If Blink Then
Me.tmrBlink.Enabled = True
End If
Me.BackColor = Color.White
Me.ForeColor = Color.FromArgb(48, 75, 139)
End Sub

Private Sub tmrBlink_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrBlink.Tick
Static NumberOfTimesBlinked As Integer

If NumberOfTimesBlinked > Me._NumberOfTimesBlink Then
NumberOfTimesBlinked = 0
Me.tmrBlink.Enabled = False
Else
If NumberOfTimesBlinked Mod 2 = 0 Then
Me.BackColor = Color.FromArgb(48, 75, 139)
Me.ForeColor = Color.White
Else
Me.BackColor = Color.White
Me.ForeColor = Color.FromArgb(48, 75, 139)
End If

NumberOfTimesBlinked += 1
End If
End Sub

Private Sub NotificationBar_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Visible = False
End Sub

Private Sub pbxClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles pbxClose.Click
Clear()
End Sub
End Class



Luego volvemos a generar el proyecto y agregamos un nuevo formulario y del cuadro de herramientas arrastramos el controls NotificationBar (se muestra cuando se vuelve a generar el proyecto)

Función para limpiar controles en Vb.net

Esta sencilla funcion permite limpiar los controles de un formulario.
  
Public Sub EmptyControls(ByVal Value As Control)
For Each control As Object In Value.Controls
If control.TabIndex = 1 Then
control.Focus()
End If
If TypeOf control Is TextBox Then
control.Text = String.Empty
End If
If TypeOf control Is ComboBox Then
control.selectedValue = String.Empty
End If

If TypeOf control Is RadioButton Then
control.Checked = False
End If

If TypeOf control Is CheckBox Then
control.Checked = False
End If

If TypeOf control Is GroupBox Then
EmptyControls(control)
End If

If TypeOf control Is Panel Then
EmptyControls(control)
End If

If TypeOf control Is TabControl Then
EmptyControls(control)
End If

If TypeOf control Is TabPage Then
EmptyControls(control)
End If

If TypeOf control Is SplitContainer Then
EmptyControls(control)
End If

If TypeOf control Is DataTable Then
control.Dispose()
End If

If TypeOf control Is DataSet Then
control.dispose()
End If
If TypeOf control Is MaskedTextBox Then
control.text = String.Empty
End If

Next
End Sub

UserControl: ToolStripNumericUpDown

Para agregar un control NumericUpDown dentrol de un ToolStrip debemos agregar una nueva clase y copiar el siguiente código.

Imports System.Windows.Forms.Design

_
Public Class ToolStripNumericUpDown : Inherits ToolStripControlHost

Public Event ValueChanged As EventHandler

Public Sub New()
MyBase.New(New NumericUpDown)
End Sub

Public ReadOnly Property ToolStripNumericUpDown() As NumericUpDown
Get
Return CType(Control, NumericUpDown)
End Get
End Property

#Region "Propiedades"
Shared _someSharedValue As Decimal = 100
Public Shared Property SomeSharedValue() As Decimal
Get
Return _someSharedValue
End Get
Set(ByVal value As Decimal)
_someSharedValue = value
End Set
End Property

Public Property Value() As Decimal

Get
Return ToolStripNumericUpDown.Value
End Get

Set(ByVal value As Decimal)
ToolStripNumericUpDown.Value = value
End Set

End Property

#End Region

#Region "Eventos"
Protected Overrides Sub OnSubscribeControlEvents(ByVal c As Control)

MyBase.OnSubscribeControlEvents(c)

Dim toolStripNumericUpDown As NumericUpDown = CType(c, NumericUpDown)

AddHandler toolStripNumericUpDown.ValueChanged, AddressOf HandleValueChanged

End Sub

Protected Overrides Sub OnUnsubscribeControlEvents(ByVal c As Control)

MyBase.OnUnsubscribeControlEvents(Control)

Dim toolStripNumericUpDown As NumericUpDown = CType(c, NumericUpDown)

RemoveHandler toolStripNumericUpDown.ValueChanged, AddressOf HandleValueChanged

End Sub

Private Sub HandleValueChanged(ByVal sender As Object, ByVal e As EventArgs)
RaiseEvent ValueChanged(Me, e)
End Sub
#End Region

End Class



ToolStripNumericUpDown es un control que tiene la misma funcioanlidad que un NumericUpDown pero dentro de un ToolsTrip.

Para ver el resultado volvemos a generar nuestro proyecto y en un formulario agregamos un ToolStrip y añadimos un nuevo elemento, se podrá apreciar que ahora aparece la opción de agregar un NumericUpDown.

sábado, marzo 05, 2011

Cuando usar ByVal y ByRef en VB.Net

Generalmente se suele usar ByVal en la programación de una función, por ejemplo

Private Sub (ByVal as ,...)

End Sub

pero tambien podemos escribir la función de la siguiente manera:

Private Sub (ByRef as ,...)

End Sub

¿Y cual es la diferencia?

Hay una gran diferencia al momento de usar ByVal y ByRef que se tiene que tener en cuenta a la hora de programar una función, y es que

El paso de argumento tipo ByVal crea una copia de la variable original, por lo que cualquier modificación hecha a dicha variable no afectará a la variable original.

El paso de argumento tipo ByRef hace una referencia de la variable original, por lo que cualquier modificación hecha a dicha variable si afectará a la variable original.

Para entender mejor en el siguiente ejemplo una variable inicializada en 10, cuyo valor se pasa a una función que lo incrementa en 1, debiendo obtener como resultado final 11.


Usando ByVal:


Dim suma As Integer = 10
fIncrementar(suma)



Private Sub fIncrementar(ByVal Value As Integer)
Value = Value + 1
End Sub


Si ejecutamos este ejemplo podremos comprobar que el item con valor 10 no aumenta a 11 porque en la función fIncrementar al usar ByVal creamos una copia de la variabel cuyo valor modificado no solo se aplicara a la variable orginal, el resutado final es que el número sigue siendo 10.



Usando ByRef:


Dim suma As Integer = 10
fIncrementar(suma)



Private Sub fIncrementar(ByRef Value As Integer)
Value = Value + 1
End Sub


Al usar ByRef prodremos comprobar que el valor de la variable suma que en un principio estuvo en 10 ahora aumento a 11, debido a que ByRef hace que la variable original se referencie en el argumento de la función fIncrementar, y cualquier cambio hecho al argumento Value se aplicará inmediatamente a la variable suma.


Otro caso en que es necesario conocer la diferencia entre Byval y ByRef es cuando queremos modificar una colección que esta siendo usado en un foreach, como se sabe no se puede agregar y tampoco eliminar items de una lista que esta siendo usado en un foreach, si se intenta hacer esto el compilador de visual estudio mostrará el siguiente error "Colección modificada; puede que no se ejecute la operación de enumeración.".

para poder realizar cambios a una colección mientras se usa en un foreach hay un truco usando ByRef .

En el siguiente ejemplo elimino un item de la colección cuando esta siendo usado en un foreach, para ello uso ByRef para hacer referencia a la lista que contiene los items, asi cualquier item que elimine en la función se actualizara en la lista original.




Dim lista As New List(Of Integer)

lista.Add(1)
lista.Add(2)
lista.Add(3)

For Each Item As Integer In lista.ToArray
If Item = 3 Then
fRemove(lista, Item)
End If
Next


Private Sub fRemove(ByRef lList As List(Of Integer), ByVal Item As Integer)
lList.Remove(Item)
End Sub


Con este truco podemos eliminar o agregar un item de la lista genérica mientras esta siendo usado en un foreach.

Reutilizar eventos en Csharp

Muchas veces los eventos de cada formulario en una aplicación MDI repiten el mismo código, por ejemplo si queremos que se abra una ventana de ayuda al presionar F1 debemos repetir el mismo código en el evento KeyDown de todos los formularios y programar la llamada a la ventana de ayuda.

Para simplificar estos temas y reutilizar código podemos optar en crear solo una función que llame a la ventana de ayuda y se ejecute cada vez que el usuario presione F1.

El siguiente ejemplo tiene un formulario MDI y dos formularios hijos, donde cada vez que se presione la tecla F1 en los formularios hijos muestre un mensaje, ademas, en la parte inferior del formulario MDI debe mostrar el nombre del formulario hijo actual.

En el formulario MDI programamos la función que abre a los formularios hijos y se asigna los eventos Activated y KeyDown para los resultados que deseamos conseguir.


//Funciones Generales

//Abrir los formularios hijos
private void fOpenWindows(Form value)
{
value.MdiParent = this;
value.Show();
//Para que los eventos de teclado se apliquen a todo el formulario
value.KeyPreview = true;
//Reutilizamos el evento activate en el formulario que vamos a abrir
value.Activated += new System.EventHandler(EvtActivate_Activated);
//Reutilizamos el evento KeyDown en el formulario que vamos a abrir
value.KeyDown += new KeyEventHandler(EvtKeyDown_KeyDown);
this.tslNomFormularioActivo.Text = value.Text;
}

//Mostrar el nombre del formulario activo
private void EvtActivate_Activated(Object sender, EventArgs e)
{
//Mostrar el nombre del formulario Activo en la parte
//inferior del MDI
this.tslNomFormularioActivo.Text = ((Form)sender).Text;
}


//Tecla presionada por el usuario
private void EvtKeyDown_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
//Mostrar un mensaje cuando se presione F1
if (e.KeyCode == Keys.F1)
{
MessageBox.Show("Usted presionó la tecla F1");
}
//Mostrar un mensaje cuando se presione F2
else if (e.KeyCode == Keys.F5)
{
MessageBox.Show("Usted presionó la tecla F5");
}
//Cuando se presione teclas combinadas que usen Ctrl + {tecla}
else if (e.Control == true)
{
//cuando el usuario presiona Ctrl + C mostrar un mensaje
if (e.KeyCode == Keys.C)
{
MessageBox.Show("Usted presionó la combinación de teclas Ctrl + C");
}
//cuando el usuario presiona Ctrl + V mostrar un mensaje
else if (e.KeyCode == Keys.V)
{
MessageBox.Show("Usted presionó la combinación de teclas Ctrl + V");
}

}
}


Código para abrir los dos formularios hijos.


private void form1ToolStripMenuItem_Click(object sender, EventArgs e)
{
Form1 wOpen = new Form1();
fOpenWindows(wOpen);
}

private void form2ToolStripMenuItem_Click(object sender, EventArgs e)
{
Form2 wOpen = new Form2();
fOpenWindows(wOpen);
}


La lógica nos sirve tanto en aplicaciones de escritorio y aplicaciones web. En Asp.Net tambien podemos reutilizar eventos.

Como vemos no se ha programado nada en los dos formularios hijos, logrando de esta manera reducir el código de nuestra aplicación.

sábado, febrero 26, 2011

Encriptar texto en vb.net con SHA512

SHA512 en un encriptador de cifrado irreversible que se encuentra en la libreria System.Security.Cryptography del .net.

Al ser de cifrado irreversible ya no se puede desencriptar. puede tener diferentes usos por ejemplo si queremos validar la contraseña de un usuario poder comparar la contraseña encriptada en la base de datos y la cadena encriptada de una caja de texto.

para usar SHA512 debemos importar las siguientes librerias


Imports System.Security.Cryptography
Imports System.Text


la siguiente funcion recibe la cabena y devuelve la cadena encriptada


Public Function EncriptarPwd(ByVal pwd As String) As String
Dim cifrador As New SHA512Managed
Dim contraseñaOriginal As Byte() = Encoding.ASCII.GetBytes(pwd)
Dim contraseñaCifrada As Byte() = cifrador.ComputeHash(contraseñaOriginal)
Dim textoContraseñaCifrada As String = Convert.ToBase64String(contraseñaCifrada)
Return textoContraseñaCifrada
End Function

Uso de SET NOCOUNT en SQLServer

En SQLServer se usa SET NOCOUNT para devolver o no la cantidad de filas afectadas por alguna sentencia o transacción ejecutada. SET NOCOUNT tiene dos estados:

SET NOCOUNT ON : Inhabilita la cuenta del número de filas afectadas por una instrucción Transact-SQL

SET NOCOUNT OFF: Habilita la cuenta del número de filas afectadas por una instrucción Transact-SQL





Cuando se usa SET NOCOUNT ON se mejora el rendimiento del servidor porque omite la devolución del numero de filas afectadas al cliente o terminal que ha realizado la consulta, pero hay casos en que es necesario obtener el número de registros afectados segun nuestra necesidad, por ello se recomienda analizar muy bien cuando usar SET NOCOUNT ON.

El siguiente ejemplo hace una consulta a la tabla customers de la base de datos Northwind usando los dos estados de SET NOCOUNT, se puede apreciar la diferencia.

Usando SET NOCOUNT ON



----- 1
GO
SET NOCOUNT ON

select * from dbo.customers
where country = 'France'
GO

Resultado:

Usando SET NOCOUNT OFF


---- 2
GO
SET NOCOUNT OFF

select * from dbo.customers
where country = 'France'
GO


Resultado:

viernes, febrero 25, 2011

Reporte con Crystal Report y Linq to Xml

En el post anterior hice un pequeño ejemplo de como guardar los datos de una tabla a un archivo Xml y luego leerlos y mostrarlos en un DataGridView.

Ahora voy a realizar un ejemplo de como mostrar un reporte de los datos contenidos en un archivo XML usando Linq. Para este ejemplo tengo un proyecto de formularios de windows llamado XmlExample, en el cual tengo un formulario diseñado como muestra la imagen.


En el proyecto XmlExample también debo crear la clase Categories.vb el cual se usará para diseñar el reporte.
Public Class Categories
Private _CategoryID As Integer = String.Empty
Private _CategoryName As String = String.Empty
Private _Description As String = String.Empty

Public Property CategoryID() As Integer
Get
Return _CategoryID
End Get
Set(ByVal value As Integer)
_CategoryID = value
End Set
End Property

Public Property CategoryName() As String
Get
Return _CategoryName
End Get
Set(ByVal value As String)
_CategoryName = value
End Set
End Property

Public Property Description() As String
Get
Return _Description
End Get
Set(ByVal value As String)
_Description = value
End Set
End Property
End Class

Agregamos un nuevo proyecto de CrystalReport




El Explorador de soluciones debe quedar asi:


En el Diseñador del reporte Seleccionamos el origen de datos.

Nos Situamos como muestra la imagen (nueva conexión de ADO.NET) y presionamos doble click.

Aparecerá una ventana donde debemos indicar que objeto deseamos usar, en nuestro caso debemos seleccionar la clase categories.vb que esta en el proyecto XmlExample y presionamos finalizar.

En el arbol de la derecha nos aparecera el orgien de datos con el cual procederemos a diseñar nuestro reporte.

En el proyecto XmlExample donde se encuentra el formulario de consulta agregamos la referencia del proyecto de CrystalReport que hemos agregado


En el formulario agregamos un boton para generar el reporte y un crystalreportView. En el boton ingresamos el siguiente código:
Dim lCategoriaXml As XDocument = XDocument.Load(txtUrl.Text)
Dim lCategorias = From lcategoria In lCategoriaXml.Descendants("Categoria") _
Select CategoryID = lcategoria.Element("CategoryID").Value, _
CategoryName = lcategoria.Element("CategoryName").Value, _
Description = lcategoria.Element("Description").Value

Dim crp As New XMLReport.RptCategories
crp.SetDataSource(lCategorias.ToList)
CrystalReportViewer1.ReportSource = crp
TabControl1.SelectedIndex = 1

Grabar Datos de una tabla a Xml y leer con Linq to XML

En algunos casos es necesario guardar los datos de una table en un archivos XML. El siguiente código muestra como grabar los datos de la tabla categories de la base de datos Northwind hacia un archivo XML en la unidad D:\




Using cn As New SqlClient.SqlConnection
cn.ConnectionString = "Data Source=xxxxx;Initial Catalog=northwind;user ID=sa;password=xxxxxxxx"
cn.Open()
Using cmd As New SqlClient.SqlCommand
cmd.CommandType = CommandType.Text
cmd.CommandText = RichTextBox1.Text
cmd.Connection = cn
Using dr As SqlClient.SqlDataReader = cmd.ExecuteReader
Dim lColumn01 As Integer = dr.GetOrdinal("CategoryID")
Dim lColumn02 As Integer = dr.GetOrdinal("CategoryName")
Dim lColumn03 As Integer = dr.GetOrdinal("Description")
Dim lCount As Integer = dr.FieldCount - 1
Dim Values(lCount) As Object
Dim lRead As Boolean = False
Dim lElement As XElement

While dr.Read
dr.GetValues(Values)
If lRead = False Then
lElement = New XElement("Categorias", _
New XElement("Categoria", New XElement("CategoryID", Values(lColumn01)), _
New XElement("CategoryName", Values(lColumn02)), _
New XElement("Description", Values(lColumn03))))
Else
lElement.Add(New XElement("Categorias", _
New XElement("Categoria", New XElement("CategoryID", Values(lColumn01)), _
New XElement("CategoryName", Values(lColumn02)), _
New XElement("Description", Values(lColumn03)))))
End If
lRead = True
End While
lElement.Save(txtUrl.Text)
MsgBox("Datos Grabados en " & txtUrl.Text)
End Using
End Using
End Using


para leer los datos del archivo XML he usado Linq to XML para mostrar los datos en el datagridview


Dim lCategoriaXml As XDocument = XDocument.Load(txtUrl.Text)
Dim lCategorias = From lcategoria In lCategoriaXml.Descendants("Categoria") _
Select CategoryID = lcategoria.Element("CategoryID").Value, _
CategoryName = lcategoria.Element("CategoryName").Value, _
Description = lcategoria.Element("Description").Value

DataGridView1.DataSource = lCategorias.ToList




puedes descargar el código del ejemplo aquí.

jueves, febrero 24, 2011

Extraer esquemas de tablas con datatable


Tal vez en alguna oportunidad se habran topado con la necesidad de extraer el esquema de una tabla (sin importar la base de datos), es decir obtener el nombre de todas sus columnas, tipo de dato, si acepto nulos, etc. . Net ofrece la posibilidad de extraer el esquema de una tabla a travez de Datatables.

Un Datatable es un objeto Ado.Net, el cual te permite representar cualquier tabla de una base de datos en modo local, donde tu puedes manipularlo a tu gusto sin que la tabla real de la base de datos se vea afectado, almenos que tu indiques que deas grabar los cambios realizados.

Este ejemplo lo voy a realizar con la tabla categories de la base de datos Northwind el cual tiene la siguiente estructura:


Ahora para obtener la estructura de la tabla categories procedemos a ejecutar la siguiente sentencia:

El resultado es:

sábado, febrero 19, 2011

Snippely, util organizador de códigos

Snippely es un útil herramienta libre que te permite organizar tus códigos de manera sencilla, ahora ya no tendrás que buscar en tus archivos aquel código que no recuerdas.

Esta herramienta es util para los desarrolladores de software, y lo puedes descargar aqui




viernes, febrero 11, 2011

XMind: Herramienta libre para diseñar diagramas

Hace tiempo vengo utilizando esta util herramienta con la cual se pueden crear diagramas, mapas conceptuales, etc.

Esta muy bueno y lo recomiendo porque a mi me ha servido.


Lo pueden descargar de la siguiente dirección: http://www.xmind.net/

jueves, febrero 10, 2011

Tablas temporales en Sql Server 2000: Optimizar su uso

Siguiendo con el post sobre las tablas temporales en SQL Server 2000 voy a dar una serie de posibles mejoras que se pueden aplicar para conseguir un mejor desempeño.

  1. Es mejor crear la tabla temporal con comandos DDL y luego insertar los datos, por ejemplo:


CREATE TABLE #TABLA1
( CODIGO INT,
NOMBRE VARCHAR(20)
)

insert into #TABLA1(CODIGO,NOMBRE) values('1','LACTEOS')
insert into #TABLA1(CODIGO,NOMBRE) values('2','BEBIDAS')


y evitar crearlo en una instrucción SELECT INTO:




SELECT categoryID,CategoryName
INTO #TABLA1
FROM CATEGORIES


Debido a que en la ultima opción los bloqueos hacia los objetos del sistema duran mas tiempo.

2. Evitar el uso de SELECT * FROM, en su lugar se debe extraer las columnas que se necesitan.

3. Insertar solo la información necesaria.

4. Borrar la tabla temporal al finalizar el conjunto de sentencias o el procedimiento almacenado. Las tablas temporales se destruyen cuando se termina la conexión pero no tendría sentido dejarlas ocupar recursos del servidor si ya no se la va a usar.

5. Cuando la cantidad de datos que maneja una tabla temporal es grande se recomienda crearle un indice para que ayude a recuperar la información.

6. Colocar la base de datos tempdb en un disco dedicado solo para esta función aumentará el rendimiento global del sistema si se hace un uso intensivo de tablas temporales.

7. No crear tablas temporales dentro de transacciones y trigger porque la concurrencia a la base de datos se verá afectada.

Diagrama de Joins en SQL Server

Aqui dejo un útil diagrama que ayuda a comprender el funcionamiento de los Joins en Sql Server.


martes, febrero 08, 2011

Tablas temporales en Sql Server 2000: alternativas recomendadas

En ocasiones el resultado que deseamos obtener de una tabla o un conjunto de tablas a travez de solo una sentencia de select resulta complicado e inviable porque los datos tienen que ser tratados y procesados a fin de mostrar la información de acuerdo a nuestra necesidad, por lo cual es inevitable el uso de tablas temporales dentro de los procedimientos almacenados.

Pero la solución en base a tablas temporales resulta perjudicial para el desempeño del servidor de base de datos por eso se recomienda no usarlas.

¿Y porque no usar tablas temporales? Las razones las menciono a continuación:

  1. las tablas temporales se crean en el tempdb y al crearlas se producen bloqueos sobre esta base de datos como por ejemplo en las tablas sysobjects y sysindexes. Los bloqueos sobre el la tempdb afectan a todo el servidor.
  2. Es necesario que se escriba sobre el disco.
  3. Al insertar datos de nuevo se produce escritura al disco (Los accesos al disco suelen ser los cuellos de botella del sistema).
  4. Al leer datos de nuevo hay que rrecurrir al disco
  5. Al borrar la tabla de nuevo hay que adquirir bloqueos sobre la base de datos tempdb y realizar operaciones en disco.
  6. Si la consulta dentro de un procedimiento almacenado tiene una tabla temporal difícilmente se reutilizará el plan de ejecución.
Siempre la mejor solución es obtener los datos a travez de una sola consulta.

Siempre es bueno saber en que momento usar tablas temporales, para ello debemos conocer bien su funcionamiento y limitaciones, tambien es bueno conocer las posibles alternativas y revisar el plan de ejecución a fin de obtener la consulta mas óptima.

Con SQL Server 2000 podemos usar variables de tablas. Las variables de tablas tienen una serie de ventajas frente a tablas temporales:

  1. Solo existe en el ámbito en que fue definido.
  2. Producen menos recompilación de procedimientos almacenados.
  3. No necesitan de bloqueos ni de tantos recursos.
  4. se crean en memoria lo cual produce menos overhead que la tabla temporal
Pero también tiene una serie de inconvenientes:

  1. Una vez declara no se puede modificar la estructura de la tabla. Por ejemplo si dentro de procedimiento almacenado hemos definido una variable de tabla con los campos código y nombre y luego de declararla queremos alterar la tabla para agregar la columna importe no nos va a permitir.
  2. Los indices tienen que ser agrupados
  3. No se pueden usar en una sola sentencia Insert into o select into. Primero se deve declarar la variable de tabla y luego insertar los datos correspondientes.
  4. No se puede usar funciones en las restricciones.

Para finalizar, viendo las ventajas y desventajas es mejor usar variables de tablas frente a tablas temporales, pero en el caso en que se procesen grandes cantidades de datos y se necesite la creación de indices es mejor una tabla temporal.

Se debe evaluar que opción es la mas recomendable para nuestro procedimiento almacenado, en el siguiente post sobre tablas temporales colocare algunas formas de optimar su uso.


domingo, febrero 06, 2011

Vb.Net: copiar el contenido de una carpeta a otra

Cuando recien estaba iniciando en el mundo de .net un realice un sencillo programa que fuese capaz de copiar el contenido de una carpeta hacia otra cada 20 segundos.

El programa lo hice en visual studio 2003, pero he realizado algunos cambios como por ejemplo la posibilidad de elegir el tiempo en que se copiaran los archivos y ademas lo he migrado a visual studio 2010.
Aquí muestro una imagen del programa.




Para descargar el ejemplo presione aqui

viernes, febrero 04, 2011

DatagridviewComboBoxColumn: celdas con items irrepetibles por fila

Ahora voy a presentar un datagridview con una columna de tipo DatagridViewComboboxColumn donde cada item seleccionado en la columna tipo combobox no se vuelva a repetir en ninguna otra fila.

Habeces es necesario hacer validaciones de este tipo, por ejemplo si en un datagridview mostramos el nombre de un trabajador donde se le debe de asignar los conceptos de pago ya sea Sueldo Básico, Horas Extras, Movilidad, etc, pero donde cada concepto sea único por fila, es decir que un trabajador no podrá tener 2 importes por concepto de sueldos basicos o 3 importes por concepto de horas extras.

Para empezar creamos la clase personal.vb


Public Class Personal
Private lcod_personal As String
Private lnom_personal As String
Private lcod_concepto As String
Private limp_concepto As Decimal


Public Property cod_personal() As String
Get
Return lcod_personal
End Get
Set(ByVal value As String)
lcod_personal = value
End Set
End Property
Public Property nom_personal() As String
Get
Return lnom_personal
End Get
Set(ByVal value As String)
lnom_personal = value
End Set
End Property
Public Property cod_concepto() As String
Get
Return lcod_concepto
End Get
Set(ByVal value As String)
lcod_concepto = value
End Set
End Property

Public Property imp_concepto() As Decimal
Get
Return limp_concepto
End Get
Set(ByVal value As Decimal)
limp_concepto = value
End Set
End Property


End Class



luego creamos una clase ListPersonal.vb que herede de una lista de personal

Public Class ListPersonal
Inherits List(Of Personal)
Sub New()

End Sub

Sub AddPersonal(ByVal lcod_personal As String, _
ByVal lnom_personal As String, _
ByVal lcod_concepto As String, _
ByVal limp_concepto As Decimal)

Dim lPersonal As New Personal
lPersonal.cod_personal = lcod_personal
lPersonal.nom_personal = lnom_personal
lPersonal.cod_concepto = lcod_concepto
lPersonal.imp_concepto = limp_concepto
Me.Add(lPersonal)
End Sub

End Class


Ahora debemos crear la clase Conceptos.vb

Public Class Conceptos
Private lcod_concepto As String
Private lnom_concepto As String

Public Property cod_concepto() As String
Get
Return lcod_concepto
End Get
Set(ByVal value As String)
lcod_concepto = value
End Set
End Property

Public Property nom_concepto() As String
Get
Return lnom_concepto
End Get
Set(ByVal value As String)
lnom_concepto = value
End Set
End Property
End Class


ademas como el caso anterior creamos la clase ListConcepto.vb que herede de una lista de Conceptos

Public Class ListConcepto
Inherits List(Of Conceptos)

Sub New()
End Sub

Sub AddConcepto(ByVal Codigo As String, ByVal Nombre As String)
Dim lconcepto As New Conceptos
lconcepto.cod_concepto = Codigo
lconcepto.nom_concepto = Nombre
Me.Add(lconcepto)
End Sub

End Class



por ultimo un formulario con un datagridview y dos botones (un boton para eliminar filas y el otro para agregar una nueva fila)


Public Class Form1
Dim lListPersonal As New ListPersonal
Dim lListConcepto As New ListConcepto
Dim lBinding As New BindingSource

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

'Creamos la Estructura del datagridview
Dim ColumnCodigo As New DataGridViewTextBoxColumn
ColumnCodigo.Name = "Código"
ColumnCodigo.HeaderText = "Codigo"
ColumnCodigo.DataPropertyName = "cod_personal"
ColumnCodigo.Width = 80
Me.DataGridView1.Columns.Add(ColumnCodigo)

Dim ColumnNombre As New DataGridViewTextBoxColumn
ColumnNombre.Name = "Nombre"
ColumnNombre.HeaderText = "Apellidos y nombre"
ColumnNombre.DataPropertyName = "nom_personal"
ColumnNombre.Width = 200
Me.DataGridView1.Columns.Add(ColumnNombre)

Dim ColumnConcepto As New DataGridViewComboBoxColumn
ColumnConcepto.Name = "Concepto"
ColumnConcepto.HeaderText = "Concepto pago"
ColumnConcepto.DataPropertyName = "cod_concepto"
ColumnConcepto.Width = 140
Me.DataGridView1.Columns.Add(ColumnConcepto)

Dim ColumnImporte As New DataGridViewTextBoxColumn
ColumnImporte.Name = "Concepto"
ColumnImporte.HeaderText = "Importe S/."
ColumnImporte.DataPropertyName = "imp_concepto"
Me.DataGridView1.Columns.Add(ColumnImporte)

Me.DataGridView1.AutoGenerateColumns = False

'Agregamos los conceptos de pago y los asignamos a la columna tipo combobox
lListConcepto.AddConcepto("", "(SELECCIONE)")
lListConcepto.AddConcepto("01", "SUELDO BÁSICO")
lListConcepto.AddConcepto("02", "ASIGNACION FAMILIAR")
lListConcepto.AddConcepto("03", "HORAS EXTRAS")
lListConcepto.AddConcepto("04", "GRATIFICACION")
lListConcepto.AddConcepto("05", "MOVILIDAD")
lListConcepto.AddConcepto("06", "REFRIGERIO")

ColumnConcepto.DataSource = lListConcepto
ColumnConcepto.DisplayMember = "nom_concepto"
ColumnConcepto.ValueMember = "cod_concepto"

'Cargamos algunos trabajadores


lListPersonal.AddPersonal("XY001", "PEREZ RODRIGUEZ, JUAN", "", 0)
lBinding.DataSource = lListPersonal
Me.DataGridView1.DataSource = lBinding

End Sub


Private Sub btnAgregar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAgregar.Click
Dim lInfo As New Personal
lInfo.cod_personal = "XY001"
lInfo.nom_personal = "PEREZ RODRIGUEZ, JUAN"
lInfo.cod_concepto = ""
lInfo.imp_concepto = 0
lBinding.Add(lInfo)

End Sub


Private Sub DataGridView1_CurrentCellChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DataGridView1.CurrentCellChanged
If DataGridView1.CurrentRow Is Nothing Then Exit Sub
If DataGridView1.CurrentRow.Cells("Concepto").Value = String.Empty Then
Me.DataGridView1.CurrentRow.ErrorText = "Debe seleccionar un concepto válido"
Else
Me.DataGridView1.CurrentRow.ErrorText = String.Empty
End If
End Sub

Private Sub DataGridView1_CellBeginEdit(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DataGridView1.CellBeginEdit
Dim lStrColumn As String = Me.DataGridView1.Columns(e.ColumnIndex).Name

'Solo es váido cuando la columna del datagridview corresponde a conceptos
If lStrColumn = "Concepto" Then


Dim lConceptosAsignados As New List(Of String)
Dim lConceptosNoAsignados As New ListConcepto

'PASO 1
'Obtenemos los conceptos que ya fueron asignados al personal

'Recorremos las filas del datagridview
For Each lInfo As DataGridViewRow In Me.DataGridView1.Rows
'Entraran a la condicion todas las filas excepto la fila que esta
'en edicion
If e.RowIndex <> lInfo.Index Then
'Obtenemos la entidad por fila
Dim lBoundItem As Personal = lInfo.DataBoundItem
If lBoundItem Is Nothing Then
Continue For
End If
'Agregamos a la lista de conceptos asignados
lConceptosAsignados.Add(lBoundItem.cod_concepto)
End If
Next

'PASO 2
'En la Celda del DataGridViewComboboxcolum que esta en edicion
'mostramos aquellos conceptos que aun no han sido asignados

Dim lBoolAsignado As Boolean = False
For Each info As Conceptos In lListConcepto
lBoolAsignado = False
'Verificamos si el concepto ha sido asignado en alguna fila
For Each Value As String In lConceptosAsignados
If info.cod_concepto = Value Then
lBoolAsignado = True
Exit For
End If
Next
'Si el concepto aun no ha sido asignado
If lBoolAsignado = False Then
lConceptosNoAsignados.Add(info)
End If
Next

'A la celda del DatagridViewComboBoxcolumn en edicion indicamos
'que solo se muestren los items que aun no han sido asignados
Dim dgvColumn As New DataGridViewComboBoxCell
dgvColumn.DataSource = lConceptosNoAsignados
dgvColumn.ValueMember = "cod_concepto"
dgvColumn.DisplayMember = "nom_concepto"
Me.DataGridView1.Item(lStrColumn, e.RowIndex) = dgvColumn
End If
End Sub

Private Sub btnEliminar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEliminar.Click
If Not (DataGridView1.CurrentRow Is Nothing) Then
lBinding.RemoveCurrent()
End If
End Sub
End Class


En el evento CellBeginEdit del datagridview es donde se van a filtrar solo aquellos conceptos que aun no han sido seleccionados y se mostraran el la celda en edición.

Puedes descargar el código del ejemplo en la siguiente dirección:
http://cid-ad089621e4982823.office.live.com/self.aspx/Ejemplos%20.Net/DataGridViewComboBoxColumnItems.rar

jueves, febrero 03, 2011

Consultas dinámicas en Sqlserver 2000

Hace algún tiempo atrás tuve que implementar en la aplicación que estuve desarrollando, consultas configurables para que el usuario puede filtrar la información de acuerdo a su necesidad, pongamos como ejemplo a la base de datos Northwind y la tabla Products, la aplicación debía consultar por cualquier columna que el usuario seleccione y filtrar la información según las coincidencias encontradas.

A mi no me pareció complejo hacer algo asi, porque solo debía armar la consulta en la aplicación y ejecutarla, pero luego me di cuenta que hacerlo de esta manera tenia dos desventajas:

- Al armar las consultas en la aplicación, esta se convertía en poco escalable y muy estática porque cualquier cambio o actualización que quisiese hacer tenia que modificar el código del programa y crear otro instalador o en todo caso reemplazar la dll donde se ha realizado el cambio.

- Las consultas que se envían desde la aplicación consumen mas recursos del servidor porque sql server tiene que cargar en memoria la sentencia a ejecutar, luego analizarla, compilarla y por ultimo ejecutarla, ocasionando que el servidor consuma mas recursos.

Los procedimientos almacenados están pre compilados en el servidor, por lo cual ejecutarlos tiene un menor costo para el servidor.

Para no armar y concatenar la sentencia en la aplicación debía ser capaz de armar la consulta en el servidor a travez de un procedimiento almacenado.

Para mi satisfacción encontré que desde la versión Sql Server 2000 existe el comando sp_executesql, capaz de ejecutar cualquier tipo de consulta que se le especifique.

El siguiente ejemplo usa sp_executesql y consiste en crear un procedimiento almacenado que consulte a la tabla Products sujeto a la condición que se envia al procedimiento.



GO
drop function [fGetCondicion]
GO
create function [dbo].[fGetCondicion]
(
@Condicion varchar(20),
@value varchar(100)
)
RETURNS VARCHAR(20)
AS
-- =============================================
-- Author: Milton Baltazar Valenzuela
-- Description: Obtiene el tipo de condicion y el valor
-- de la condicion para devolver la condicion
-- en el formato requerido
-- =============================================
BEGIN
RETURN (
SELECT
CASE @Condicion
WHEN 'like' THEN @Condicion + '''%' + @value + '%'''
WHEN '=' THEN @Condicion + ' ' + @value
WHEN '<>' THEN @Condicion + ' ' + @value
WHEN '>' THEN @Condicion + ' ' + @value
WHEN '<' THEN @Condicion + ' ' + @value ELSE null END) END GO GO drop procedure usp_get_products GO create procedure usp_get_products ( @productID int ,@ProductName varchar(40) ,@CategoryID int ,@UnitPrice money ,@Condicion varchar(100) ) AS BEGIN Declare @ls_query nvarchar(4000) Declare @li_error int Declare @ls_error varchar(1000) set @ls_query='select productID, ProductName, CategoryID, UnitPrice from dbo.Products' if @productID is not null begin set @ls_query = @ls_query + ' where productID ' + dbo.fGetCondicion(@Condicion,@productID) end else if @ProductName is not null begin set @ls_query = @ls_query + ' where ProductName ' + dbo.fGetCondicion(@Condicion,@ProductName) end else if @CategoryID is not null begin set @ls_query = @ls_query + ' where CategoryID ' + dbo.fGetCondicion(@Condicion,@CategoryID) end exec @li_error = sp_executesql @ls_query,N' @productID int ,@ProductName varchar(40) ,@CategoryID int ,@Condicion varchar(100)' ,@productID ,@ProductName ,@CategoryID ,@Condicion ; if @li_error <> 0
begin
set @ls_error = 'Error en la consulta. '
RaisError(@ls_error,16,1)
end
END
GO


----------------------------------------------------
--ejecutamos el procedimiento almacenado
----------------------------------------------------

exec usp_get_products 10,null,null,null,'='

exec usp_get_products null,'',null,null,'like'

exec usp_get_products null,'Chai',null,null,'Like'


GO


En resumen he creado un procedimiento almacenado que me permita consultar
cualquier campo de acuerdo a la condición que le envío, es cierto que al código no esta al 100% perfeccionado pero solo quiero dar la idea de que se puede hacer con sp_executesql.

DatagridView con origen de datos Listas Genericas y columnas Ordenables

Cuando se usa un datatable con origen de datos en un DatagridView por defecto queda la posibilidad de ordenar de manera ascendente o descendente los datos haciendo click en los encabezados de la columnas.

Pero cuando se usa listas genéricas como origen de datos para un Datagridview la posibilidad de ordenación queda deshabilitado.

para solucionar este problema se debe crear una clase SortableBindingList.vb que herede de BindingList


Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Text
Imports System.Reflection

Public Class SortableBindingList(Of T)
Inherits BindingList(Of T)
Private _isSorted As Boolean
Private _sortDirection As ListSortDirection
Private _sortProperty As PropertyDescriptor

'This override shows the binded object, that our list supports sorting
Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
Get
Return True
End Get
End Property

'And that it can sort bi-directional
Protected Overrides ReadOnly Property SortDirectionCore() As ListSortDirection
Get
Return _sortDirection
End Get
End Property

'And that it can sort by T typed object's properties
Protected Overloads Overrides ReadOnly Property SortPropertyCore() As PropertyDescriptor

Get
Return _sortProperty
End Get
End Property

'This is the method, what gets called when the sort event occurs in the bound object
Protected Overloads Overrides Sub ApplySortCore(ByVal prop As PropertyDescriptor, ByVal direction As ListSortDirection)
Dim items As List(Of T) = TryCast(Me.Items, List(Of T))

If items IsNot Nothing Then
Dim pc As New PropertyComparer(Of T)(prop.Name, direction)
items.Sort(pc)
_isSorted = True
_sortDirection = direction
_sortProperty = prop
Else
_isSorted = False
End If
OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
End Sub

'This shows if our list is already sorted or not
Protected Overloads Overrides ReadOnly Property IsSortedCore() As Boolean
Get
Return _isSorted
End Get
End Property

'Removing the sort
Protected Overrides Sub RemoveSortCore()
_isSorted = False
End Sub

'Sub New(ByVal list As ICollection(Of T))
' MyBase.New(list)
'End Sub
End Class

Public Class PropertyComparer(Of T)
Implements IComparer(Of T)
Private _property As PropertyInfo
Private _sortDirection As ListSortDirection

Public Sub New(ByVal sortProperty As String, ByVal sortDirection As ListSortDirection)
_property = GetType(T).GetProperty(sortProperty)
Me._sortDirection = sortDirection
End Sub

Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements IComparer(Of T).Compare
Dim valueX As Object = _property.GetValue(x, Nothing)
Dim valueY As Object = _property.GetValue(y, Nothing)

If _sortDirection = ListSortDirection.Ascending Then Return Comparer.[Default].Compare(valueX, valueY)

Return Comparer.[Default].Compare(valueY, valueX)
End Function
End Class



SortableBindingList reemplazará a List(Of ) o a cualquier tipo de coleccion que use. Como este ejemplo lo hago de la base de datos Northwind y la tabla Categories tengo que crear una clase con las columnas que deseo consultar.

procedemos a crear la clase Categories.vb


Public Class Categories
Private lCategoryId As String = String.Empty
Private lCategoryName As String = String.Empty
Private lDescription As String = String.Empty

Public Property CategoryId() As String
Get
Return lCategoryId
End Get
Set(ByVal value As String)
lCategoryId = value
End Set
End Property

Public Property CategoriName() As String
Get
Return lCategoryName
End Get
Set(ByVal value As String)
lCategoryName = value
End Set
End Property

Public Property Description() As String
Get
Return lDescription
End Get
Set(ByVal value As String)
lDescription = value
End Set
End Property

End Class



ahora voy a crear una clase ListCategories.vb que herede de SortableBindingList


Public Class ListCategories
Inherits SortableBindingList(Of Categories)

Sub New()
End Sub

End Class



ahora cuando quiera usar una colección de la clase categories.vb solo tengo que instanciar de la clase ListCategories y ya no de List(Of)

ejmplo:

Dim List as New ListCategories

o

Dim List as New SortableBindingList(Of categories)

lo cual reemplazaria a:

Dim List as New List(Of categories)

Ahora creamos un formulario con un datagridview y consultamos de la base de datos Northwind y la tabla Categories y colocamos el siguiente código en el load del formulario.


Dim lList As New ListCategories

Using cn As New SqlConnection("Data Source=XXXX;Initial Catalog=BD;User ID=sa;Password=xyzxyz")
Using cmd As New SqlCommand
cmd.CommandType = CommandType.Text
cmd.CommandText = "select categoryId, categoryName, Description from Categories"
cmd.Connection = cn
cn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader
If dr.HasRows = True Then
Dim lColumn0 As Integer = dr.GetOrdinal("categoryId")
Dim lColumn1 As Integer = dr.GetOrdinal("categoryName")
Dim lColumn2 As Integer = dr.GetOrdinal("Description")
Dim lCount As Integer = dr.FieldCount - 1
Dim Values(lCount) As Object
Dim lEntidad As Categories
While dr.Read
dr.GetValues(Values)
lEntidad = New Categories
lEntidad.CategoryId = Values(lColumn0)
lEntidad.CategoriName = Values(lColumn1)
lEntidad.Description = Values(lColumn2)
lList.Add(lEntidad)
End While
End If
End Using
cn.Close()
End Using
End Using

DataGridView1.DataSource = lList


ahora ejecutamos y podemos visualizar que el datagridview se carga teniendo asociado como origen de datos una lista generica de tipo SortableBindingList y tambien podemos apreciar que se puede ordenar las columnas haciendo click en ellas.

espero haya sido de utilidad, hasta la próxima.

lunes, enero 03, 2011

Cacoo, Un diagramador en la nube gratis

Hace algun tiempo vengo usando esta util herramienta que me ha sacado de apuros en muchas ocasiones, se trata de Cacoo, un diagramador online donde se puede diseñar cualquier tipo de diagrama, ademas te ofrece la ventaja de almacenar tus creaciones.

Muchas veces deseamos crear diagramas pero no contamos con el software adecuado, y los programas encargados para esto muchas veces resultan muy pesados y no vale la pena tenerlos instalados en nuestro ordenador para realizar tareas que no son tan habituales. al ser esta una herramienta que esta en la web resulta muy útil en momentos de apuros.

Para poder usar Cacoo debes registrarte sin ningun costo en la siguiente dirección http://cacoo.com/ .

Aqui dejo una imagen de la IDE de Cacoo con un sencillo gráfico.





Espero que haya sido de utilidad, hasta luego.