Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

sábado, 19 de mayo de 2012

Arrays en C#

Encontré este tutorial sobre arrays en C#. Noté que le faltó mencionar un detalle: Según la documentación de msdn, un array no son sólo posiciones en memoria, son objetos que heredan de System.Array.
Primeo miramos la declaración de la clase Array:



La clase Array hereda los métodos de muchas otras clases, entre ellas IEnumerable. Es decir: todos los métodos que se pueden usar en IEnumerable, se pueden usar también con arrays.

A continuación un pequeño ejemplo:
Tengo un array de Integers (en realidad pueden ser cualquier tipo de datos: byte, double, DateTime, etc), quiero eliminar los que se repiten y convertirlos a cadenas de caracteres:

int[] misNumeros; // Contiene muchos números.
strings[] misCadenas; // aquí guardo los que no se repiten, luego de convertirlos a strings.

misCadenas = misNumeros.Distinct().Select(c => c.ToString()).ToArray();

Como los métodos Select y Distinct devuelven un IEnumerable, lo convierto a Array usando el método ToArray().

Otro ejemplo: creo un array con números del 0 al 100, realizo algunas operaciones aritméticas cualquiera, elimino los repetidos, los ordeno en orden descendente, y finalmente los convierto a un array de cadena de caracteres:

int[] nums = Enumerable.Range(0, 100).ToArray();

nums = nums.Select(c => c * DateTime.Now.Millisecond / DateTime.Now.Second + DateTime.Now.Millisecond).ToArray();

string [] calculatedNums = nums.Distinct().OrderByDescending(c => c).Select(c => c.ToString()).ToArray();

Nótese cómo siempre utilizo ToArray() para convertir de IEnumerable a Array.

viernes, 13 de abril de 2012

VB6 Code Snippet: Colocar el cero delante luego de convertir de Double a String

Quería convertir varios valores decimales, guardados en variables tipo Double ó Single, a cadenas de caracteres usando la función Str$ (el "$" le indica que devuelva un String, no un Variant). Pero sucedía que al querer convertir valores entre cero y uno, me eliminaba el cero a la izquierda del punto decimal. Por ejemplo, si convertía 0.12, obtenía ".12" en lugar de "0.12".

La manera de arreglarlo es evaluar el primer caracter de la cadena de caracteres resultante de la conversión, si es un punto se le añade un cero a la izquierda:

Dim miString as String
Dim miNum as Double

...  coding coding coding...

miString = Str$(miNum)
If AscB(Trim$(miString)) = 46 Then miString = "0" & Trim$(miString)

El código Ascii del punto es 46. Utilizo la función AscB porque es más rápida que Asc (AscB devuelve los bits del primer caracter, Asc además los convierte a Ansi). Uso la función Trim$ para eliminar cualquier espacio en blanco al inicio y al final de miString.

Trim devuelve un Variant. Trim$ devuelve un String.

Más información sobre optimización en el uso de Strings en Visual basic 6 aquí.

jueves, 29 de marzo de 2012

Access y C#: Error de Síntaxis en la expresión "Insert Into" (otra vez)

Desde una app en C# intentaba ejecutar este query a una base de datos en Access:
string query = "INSERT INTO MiTabla (miID, Date) VALUES (1, '29/03/2012')";

Y me saltaba esta excepción:



El query correcto es:
string query = "INSERT INTO MiTabla (miID, [Date]) VALUES (1, '29/03/2012')";

Damn you, Access.

miércoles, 14 de marzo de 2012

VB6: Error '-2147217887 (80040e21)' en tiempo de ejecución

El mensaje completo de mi error es:
"Error '-2147217887 (80040e21)' en tiempo de ejecución. Error en el método 'DataSource' del objeto 'IMSHFlexGrid'"


El error saltaba en esta línea de código:
miHierarchicalFlexGrid.DataSource = unRecordset

Lo curioso es que el recordset mostraba sus datos correctamente en varias cajas de texto. Algo debía haber en el recordset que no era compatible con el Hierarchical FlexGrid al que quería enlazarlo.

La base de datos que estoy utilizando es AdventureWorks para SQL Server 2005. Ésta es una base de datos de ejemplo y se puede descargar de Internet. La tabla a la que se conecta el recordset es "Persons.Contact":


Y mirando la tabla Persons.Contact noté que uno de los campos tiene un tipo de datos que no había visto antes: XML. Decidí cambiarlo a VarChar y correr nuevamente el programa.
Ya no sale el error y el recordset sí se puede mostrar en el Hierarchical FlexGrid.

Conclusión: El Hierarchical FlexGrid de Visual Basic 6 no es compatible con tipos de datos XML.

miércoles, 7 de marzo de 2012

C#: Rutina para Eliminar FIlas Repetidas en un DataGridView

Antes ya lo había hecho para un Hierarchical FlexGrid en VB6. Éste es el mismo algoritmo, pero aplicado a un DataGridView (llamado dgView) y en C#:

int m = 0;  // Apunta a la fila actual
int n = dgView.Rows.Count - 1;  // cantidad de filas en el DataGridView
int k;
string estaFila, unaFila;

while (m < n)
{
    k = 1;
    estaFila = String.Empty;

     // Relleno la cadena con los datos de toda la fila
    for (int i = 0; i < dgView.Columns.Count; i++)
        estaFila = String.Concat(estaFila, dgView.Rows[m].Cells[i].Value.ToString());

    while (k < n)
    {
        unaFila = String.Empty;  // Fila a comparar

        for (int i = 0; i < dgView.Columns.Count; i++)
            unaFila = String.Concat(unaFila, dgView.Rows[k].Cells[i].Value.ToString());

        if (String.Compare(estaFila, unaFila) == 0 && k != m)
        {
            dgView.Rows.RemoveAt(k); // Si son iguales remuevo unaFila solamente
            n--;     // Tamaño actual del DataGridView, al remover disminuye en uno
        }
        k++;
    }
    m++;
}

sábado, 18 de febrero de 2012

Tutorial: Creando Arrays de Controles con sus Eventos en C# y usándolos al estilo VB6

Al programar en C# siempre eché en falta el no poder crear arrays de controles al estilo Visual Basic 6.  Fue después de probar un poco que descubrí que .Net, además de admitir arrays de controles, también se les puede asignar eventos de tal forma que el código se comporte de modo muy similar al viejo VB6, donde se identifican a los controles del array mediante un índice.
El tutorial ya está subido a la sección de C# de: http://electronicaamartillazos.co.nr/
Link directo Aquí.
Un Mirror: Aquí.

Incluye cómo enviar un mensaje a otra ventana (formulario) desde un control creado dentro del Array.

lunes, 13 de febrero de 2012

Añadir y remover columnas, y manipular datos de un DataTable en C#

Tengo un método que me devuelve un DataTable con los siguientes datos:

 


Lo que necesito es tener en el DataTable sólo las cadenas que están después del primer espacio en blanco (por ejemplo, si se desea separar apellidos y nombres).

Las columnas del DataTable no son necesariamente son del tipo string (pueden ser Date Time o tipos numéricos), si todas las columnas fueran tipo String sería fácil extraer las subcadenas. Cambiar el valor de la propiedad ValueType de las columnas no funciona cuando el DataTable ya tiene datos.

Lo que se me ocurrió fue crear cuatro columnas nuevas que almacenaran datos tipo string, leer el dato correspondiente de cada fila de cada columna original, convertirlo a cadena de caracteres, extraer las subcadenas que necesito, copiarlas a la fila respectiva de una de las columnas nuevas.

Este es el método que me devuelve el DataTable sólo con las subcadenas (las que están después del espacio en blanco):


            DataTable dTable = algunMetodo();

            int col = dTable.Columns.Count;

            dTable.Columns.Add("T11", typeof(string));
            dTable.Columns.Add("T22", typeof(string));
            dTable.Columns.Add("C11", typeof(string));
            dTable.Columns.Add("C22", typeof(string));

            for (int i = 0; i < dTable.Rows.Count; i++)
                for (int j = 0; j < col; j++)
                {
                    string subcadena;
                    int n;

                    subcadena = dTable.Rows[i][j].ToString();

                    n = subcadena.IndexOf(' ');
                    subcadena = subcadena.Substring(n + 1);
                    dTable.Rows[i][j + col] = subcadena;
                }

            // esta parte es opcional si no se desean las columnas originales
            for (int j = 0; j < col; j++)
                dTable.Columns.Remove(dTable.Columns[j]);
            
            return dTable;

Este método recorre cada celda del DataTable, copia su contenido en una variable tipo string llamada "subcadena" y usa la instrucciones IndexOf y Substring para extraer la parte de la cadena que está después del primer espacio en blanco, la cual copia a la celda respectiva en las nuevas columnas añadidas.

Y así es como queda el DataTable:



jueves, 12 de enero de 2012

Copiar los valores de las celdas seleccionadas de un DataGridView a otros controles

Tengo el siguiente DataGridView enlazado a un DataTable:



Cuya propiedad "SelectionMode" es igual a "FullRowSelect". Lo que quiero hacer es que cuando selecciones una fila, los valores de sus celdas se copien a otros controles.

Para ello, en el evento "SelectionChanged" pongo el siguiente código para C# (he llamado a mi DataGridView "dGrid):

if (dGrid.DataSource != null && dGrid.Rows.Count > 0 && dGrid.SelectedRows.Count > 0)
            {
                textBox1.Text = dGrid.SelectedRows[0].Cells[0].Value.ToString();
                comboBox.Text = dGrid.SelectedRows[0].Cells[0].Value.ToString();  
                DateTimePick.Value = Convert.ToDateTime(dGrid.SelectedRows[0].Cells[1].Value);
                textBox2.Text = dGrid.SelectedRows[0].Cells[2].Value.ToString();               
            }

En el código tengo dos TextBoxes, un ComboBox y un DateTimePicker. Los índices de las celdas empiezan desde cero. Al asignarle un valor tipo fecha al DateTimePicker hay que tener cuidado que el valor en la celda del DataGridView tenga el formato correcto o tirará una excepción.

miércoles, 4 de enero de 2012

Transacciones a una base de datos en Access

Tenía que escribir un programita que accediera (y algunas veces modificara) a los datos en una base de datos local en Access. Esta base de datos también era leída y modificada desde otra aplicación, y era muy probable que ambos programas accedieran a la base de datos al mismo tiempo, e intentaran modificarla.
Tenía que evitar que una de las aplicaciones corrompiera la lectura/escritura de la otra, así que les hice la pregunta a la gente de StackOverflow ¿cuál es la mejor manera de atacar este problema?
Me respondieron que usara Transacciones.

Mi programa tenía que leer data de la base de datos y guardarla en un DataTable. Buscando tutoriales para usar Transacciones con un DataAdapter me topé con la Web del Guille.
El problema era que Guille escribió su tutorial pensando en SQL Server, no para Access. Cuando quise usar su código usando los objetos OleDbConnection, OleDbDataAdapter, OleDbCommandBuilder y OleDbCommand me tiraba varias excepciones.

Leyendo un poco la documentación de MSDN y, mediante prueba y error, pude finalmente crear un método para usar un Select a una base de datos en Access con Transacciones y que devolviera un DataTable:

private DataTable miDatatable(string sqlQuery)
        {
            string connString = String.Concat("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=", rutaAmiBaseDeDatos);

            using (OleDbConnection connDB = new OleDbConnection(connString))
            {
                OleDbDataAdapter dAdapter;
                OleDbCommandBuilder cBuilder;
                OleDbCommand command = new OleDbCommand();
                DataTable dTable = new DataTable();
                OleDbTransaction trans = null;

                try
                {
                    connDB.Open();
                    // no acepta IsolationLevel.Serializable, Snapshot o RepeatableRead
                    trans = connDB.BeginTransaction(IsolationLevel.ReadCommitted);

                    command.Connection = connDB;
                    command.Transaction = trans;
                    command.CommandText = sqlQuery;

                    dAdapter = new OleDbDataAdapter(sqlQuery, connDB);
                    cBuilder = new OleDbCommandBuilder(dAdapter);

                    dAdapter.SelectCommand.Transaction = trans;
                    dAdapter.Fill(dTable);
                    trans.Commit();
                }

                catch
                {
                    try
                    {
                        trans.Rollback();
                    }
                    catch { } // transacción inactiva
                }

                return dTable;
            }
        }


La variable sqlQuery puede tener cualquier sentencia SQL que sea un Select, como por ejemplo "Select * from miTabla" e incluir "Where", "Join", "Distinct" etc.

miércoles, 21 de diciembre de 2011

Mi Repositorio de Códigos Fuente

Son códigos que ya he publicado anteriormente, pero ahora en un sólo lugar, en mi cuenta en mygnet para que todo el mundo los descargue, los copie, los modifique, los analice, los traduzca a otros lenguajes, y haga todo lo que les plazca con ellos :D