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, 14 de diciembre de 2013

Cómo Convertir de long a LPCWSTR (puntero largo a Wide String constante) en Visual C++

Todo empezó cuando quise aprender a usar las apis GetWindowRect y GetClientRect. La primera devuelve las coordenadas que ocupa la ventana, la segunda devuelve las coordenadas que ocupa la ventana sin la barra de título.

Como práctica, quise calcular el alto de la barra de título de una ventana.

El valor calculado es del tipo long. Luego debía convertirlo a LPCWSTR para pasarlo como parámetro a la api setWindowText que lo escribirá en lugar del título de la ventana.

He usado la información que hallé en estas webs:
La función ltoa (reemplazada por _ltoa)
Convertir de char* a LPCWSTR
La función MultiByteToWideChar

El código es el siguiente:

RECT r1, r2;

GetWindowRect(hWnd, &r1);

GetClientRect(hWnd, &r2);

long alto1 = r1.bottom - r1.top;

long alto2 = r2.bottom - r2.top;

long alto3 = alto1-alto2;

// convertir long a Wide WCHAR*

int size=sizeof(long)*8+1;

char* texto = new char[size];

WCHAR* texto2 = new WCHAR[size];

_ltoa(alto3,texto,10);

MultiByteToWideChar(CP_UTF8, 0, texto, -1, texto2, size);

// fin de la conversión. Para convertir de WCHAR* a LPCWSTR (puntero largo a Const Wide String) basta un casteo

SetWindowText(hWnd, (LPCWSTR)texto2);


En el caso de una aplicación tipo win32, coloqué este código dentro de la función que procesa los mensajes de la ventana (WndProc), exactamente en el mensaje que se envía al crearla:


Al ejecutar el programa, mostrará el alto de la barra de título en el texto de... la barra de título.


sábado, 23 de noviembre de 2013

Implementación Thread-safe de una clase Singleton

Entrada original de WhatILearntToday: "El patrón Singleton se usa cuando queremos que una clase permita definir una, y sólo una, instancia de esa misma clase".

La implementación WhatILearntToday de una clase Singleton Thread-Safe es la siguiente:

public class SingletonClass
    {
        private SingletonClass()
        {
        }

        //Implementation #1 NO Thread-Safe: puede fallar si varios hilos intentan instanciarla al mismo tiempo
        //static SingletonClass _instance = null;
        //public static SingletonClass GetInstance()
        //{
        //    if (_instance == null)
        //    {
        //        _instance = new SingletonClass();
        //    }
        //    return _instance;
        //}

        //Implementation #2 Thread-safe
        static readonly SingletonClass _instance = new SingletonClass();

        public static SingletonClass GetInstance()
        {
            get
            {
                return _instance;
            }
        }

        public string GetWelcomeMessage()
        {
            return "Welcome to the Singleton world!! ";
        }
    }

Mi implementación (también Thread-Safe) es:

public class SingletonClass
    {
        private SingletonClass()
        {
        }

        static readonly SingletonClass _instance = null;
        static object lockThis;

        public static SingletonClass GetInstance()
        {
            lock(lockThis)
            { 
                 if (_instance == null)
                         _instance = new SingletonClass();
            }  
           return _instance;           
        }

        public string GetWelcomeMessage()
        {
            return "Welcome to the Singleton world!! ";
        }
    }

miércoles, 23 de octubre de 2013

Cómo evadir la limitación de tamaño de una ventana en Windows

Por diseño, Microsoft Windows limita el tamaño de las ventanas a (en píxeles):
(12 + ancho de la resolución de pantalla) x (12 + alto de la resolución de pantalla)

Más información en StackOverflow.

Por allí hallé quienes afirman que se puede superar esta restricción. Y entonces me topé con este código, de la misma Microsoft, en Visual Basic 6.

Lo copio aquí para que no se vaya a perder:

En un módulo bas:

 Option Explicit

      Private Const GWL_WNDPROC = -4
      Private Const WM_GETMINMAXINFO = &H24

      Private Type POINTAPI
          x As Long
          y As Long
      End Type

      Private Type MINMAXINFO
          ptReserved As POINTAPI
          ptMaxSize As POINTAPI
          ptMaxPosition As POINTAPI
          ptMinTrackSize As POINTAPI
          ptMaxTrackSize As POINTAPI
      End Type

      Global lpPrevWndProc As Long
      Global gHW As Long

      Private Declare Function DefWindowProc Lib "user32" Alias _
         "DefWindowProcA" (ByVal hwnd As Long, ByVal wMsg As Long, _
          ByVal wParam As Long, ByVal lParam As Long) As Long
      Private Declare Function CallWindowProc Lib "user32" Alias _
         "CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
          ByVal hwnd As Long, ByVal Msg As Long, _
          ByVal wParam As Long, ByVal lParam As Long) As Long
      Private Declare Function SetWindowLong Lib "user32" Alias _
         "SetWindowLongA" (ByVal hwnd As Long, _
          ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
      Private Declare Sub CopyMemoryToMinMaxInfo Lib "KERNEL32" Alias _
         "RtlMoveMemory" (hpvDest As MINMAXINFO, ByVal hpvSource As Long, _
          ByVal cbCopy As Long)
      Private Declare Sub CopyMemoryFromMinMaxInfo Lib "KERNEL32" Alias _
         "RtlMoveMemory" (ByVal hpvDest As Long, hpvSource As MINMAXINFO, _
          ByVal cbCopy As Long)

      Public Sub Hook()
          'Start subclassing.
          lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, _
             AddressOf WindowProc)
      End Sub

      Public Sub Unhook()
          Dim temp As Long

          'Cease subclassing.
          temp = SetWindowLong(gHW, GWL_WNDPROC, lpPrevWndProc)
      End Sub

      Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, _
         ByVal wParam As Long, ByVal lParam As Long) As Long
          Dim MinMax As MINMAXINFO

          'Check for request for min/max window sizes.
          If uMsg = WM_GETMINMAXINFO Then
              'Retrieve default MinMax settings
              CopyMemoryToMinMaxInfo MinMax, lParam, Len(MinMax)

              'Specify new minimum size for window.
              MinMax.ptMinTrackSize.x = 200
              MinMax.ptMinTrackSize.y = 200

              'Specify new maximum size for window.
              MinMax.ptMaxTrackSize.x = 500
              MinMax.ptMaxTrackSize.y = 500

              'Copy local structure back.
              CopyMemoryFromMinMaxInfo lParam, MinMax, Len(MinMax)

              WindowProc = DefWindowProc(hw, uMsg, wParam, lParam)
          Else
              WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, _
                 wParam, lParam)
          End If
      End Function


El truco es fácil: donde están los números en rojo, escribimos nuestros números enormes favoritos. Yo le puse por código a una ventana (Form) dimensiones de 2050*1600 píxeles, en una resolución de pantalla de 1024*768 (la restricción por defecto sería 1036*780).

Para hacer esto en el form ponemos:

      Option Explicit

      Private Sub Form_Load()
          'Save handle to the form.
          gHW = Me.hwnd

          'Begin subclassing.
          Hook 
          ' le ponemos un tamaño ENORME 
          Me.Height = 1600
          Me.Width = 2050
      End Sub

      Private Sub Form_Unload(Cancel As Integer)
          'Stop subclassing.
          Unhook
      End Sub 


Y funcionó.

Ya no intenté con dimensiones mayores porque una ventana con aproximadamente el doble de alto y ancho que la resolución de pantalla parece ser suficientemente grande. Deduzco que el máximo que puede aguantar el sistema operativo depende de la memoria máxima que le puede asignar a semejante ventanón y antes que la RAM empiece a echar humo.

Como son puras llamadas a Apis de WIndows, se puede traducir este código a Visual C++ o a .Net usando DLLImport.

sábado, 12 de octubre de 2013

Cómo importar datos con carácteres internacionales desde xls a SQLite

Tengo el siguiente archivo xls:


y quiero pasar los datos a una tabla en SQLite.
Después de probar distintas aplicaciones para SQLite, descubrí que ninguna importa directamente desde el formato xls (al menos no las que son gratuitas), pero sí importan desde el formato csv.

Tanto MSExcel como OpenOffice Calc pueden exportar datos al formato csv. Pero hay que tener en consideración un par de detalles para no perder los carácteres internacionales.

Para este ejemplo estoy usando OpenOffice Calc 3, pero el procedimiento es muy similar para MS Excel.

En "Guardar Como" elegimos el formato csv. Aparecerá la siguiente ventana:


Los datos deben exportarse con la codificación UTF8 y estar entrecomillados.

Para importar los datos a una base de datos en SQLite, el mejor programa que hallé es el Add-On para Firefox SQLite Manager.

Una vez instalado, lo ejecutamos desde Firefox -> Herramientas -> SQLite Manager (yo uso la versión 0.8.0) y cargamos nuestra base de datos, o la creamos.
Si la creamos no añadimos ninguna tabla, los datos importados crearán una automáticamente (mi base de datos se llama "Prueba01").



Luego vamos a la opción Base de Datos -> importar, o al ícono celeste con la flechita al costado del ícono de Abrir:



Elegimos la pestaña "CSV". Con el botón "Seleccionar Archivo" escogemos el archivo csv donde hemos exportado los datos desde xls. Las opciones a elegir son:
- Tipo de Codificación: UTF-8
- La primera fila contiene los nombres de las columnas
- Campos separados por coma
- Campos limitados por comillas dobles, siempre.
- Le cambié el nombre a la nueva tabla de "sample sales" a "sample_sales". Es mejor que las tablas no tengan nombres con espacios.

Presionamos OK. Aparecerá esta ventanita:



Oh! ¡Aquí hay algo qué resaltar!

En mi caso, como los datos de "CategoryID" se repiten, debo deseleccionar las opciones "Clave Primaria" "Auto Inc" y "única" si no botará error (y uno bastante feo). Estas opciones se seleccionan sólo si los datos correspondientes son únicos, no hay valores en blanco, y pueden convertirse a enteros.

En una bse de datos SQLIte es mejor no usar el formato DateTime. Lo mejor es guardar las fechas como texto, y recién realizar la conversión en nuestra aplicación. Así se evitan excepciones que se producen en el driver de SQLite y no en nuestro código (esta recomendación es de la misma web de SQLite, y la aprendí después de jugar un poco con el C# y aventar algunas hermosas excepciones de formato...).

Si todo sale bien, nuestros datos se verán así, ya dentro de la base de datos:


Si no se declara una clave primaria, el SQLite Manager nos creará una :D en este caso es "rowid". Y como se puede ver, los carácteres internacionales se conservan intactos.

Ya para trastear administrar la base de datos, prefiero usar el SQLite Studio.

jueves, 8 de agosto de 2013

Cómo ignorar los acentos al hacer una búsqueda en una base de datos en MS Access (mdb) con C#

Tengo el siguiente código:

string nombre = nombreABuscarenlaBD;

string query = String.Concat("Select * from Tabla1 where campoNombre like '", nombre, "'");

Este query funciona mientras no se desee buscar un nombre que incluya acentos como "María" ingresando "Maria" . Lo peor: puede que el usuario ni siquiera sepa si en la base de datos los nombres están guardados con acentos o no.

Para que la búsqueda devuelva todas las coincidencias con y sin acentos, se debe hacer el siguiente cambio a la variable tipo string que contiene el criterio de búsqueda (la idea es sacada de este foro):

nombre = nombre.Replace("a", "[aá]").Replace("e", "[eé]").Replace("i", "[ií]").Replace("o", "[oó]").Replace("u", "[uú]").Trim();

Si se desea ignorar también las diéresis, será:

nombre = nombre.Replace("a", "[aäá]").Replace("e", "[eëé]").Replace("i", "[iïí]").Replace("o", "[oöó]").Replace("u", "[uüú]").Trim();

miércoles, 24 de julio de 2013

Tutorial: Script en Python para montar particiones en Ubuntu 12.04

Nueva sección de Python en Electrónica a Martillazos con un nuevo tutorial: Script para montar particiones en Ubuntu.
Sólo lo he podido probar con particiones NTFS, y sin espacios en blanco en el nombre de la carpeta dentro de /media/ donde se monta la partición.
Un enlace directo
Un mirror

:D

martes, 16 de julio de 2013

Seleccionar registros entre dos fechas en SQL Server 2005

Para seleccionar registros que se encuentren entre dos fechas almacenadas en un campo específico tipo DateTime, se debe crear un script de la siguiente forma:

Select * from tabla1 where campoFecha > Convert(char(19), '11/01/2010', 113) and campoFecha < Convert(char(19), '11/08/2010', 113)

La fecha debe estar en el formato dd/MM/yyyy HH:mm:ss (formato de fecha y 24 horas. Suele arrojar error si la hora está en el formato am/pm).
En C# la conversión de una fecha almacenada en una variable tipo DateTime al formato de 24 horas se realiza con la siguiente instrucción sacada de la web de MSDN:

DateTime date1 = alguna fecha;
string sDate = date1.ToString("dd/MM/yyyy HH:mm:ss", System.Threading.Thread.CurrentThread.CurrentCulture);

Para armar el script simplemente se concatena utilizando la variable sDate.

Para eliminar, el script tendrá la forma:
Delete from tabla1 where campoFecha = Convert(char(19), '11/08/2010 15:00:00', 113)

En ambos casos, la hora es opcional.
Más información acerca de la función Convert de Transact-sql en este enlace.
La función Convert de transact-sql que utilizo es sacada de Onglasses.
Una lista completa del uso de la función Convert de transact-sql en este enlace.

martes, 11 de junio de 2013

Cómo mantener la misma fila seleccionada de un DataGridView sin importar si cambia su índice (Index)

Tengo un DatagridView llamado dGridView como el siguiente:



El DataGridView tiene su propiedad SelectMode igual a FullRowSelect.
Las filas no contienen datos únicos y, en otra parte del programa, cambio su propiedad Multiselect de true a false y viceversa.
Al cambiar el valor de la propiedad Multiselect, la fila o filas seleccionadas se deseleccionan (se pierde la selección), además el usuario puede hacer click en las cabeceras de las columnas lo cual cambia el orden y los índices de las filas (hacer click en las cabeceras de las columnas no deselecciona ninguna fila).
Lo que deseo hacer es que la última fila seleccionada por el usuario siempre se mantenga seleccionada a pesar de que se haga click en las cabeceras de las columnas, la filas ya no estén en el mismo orden, y se cambie el valor de MultiSelect (u otra propiedad que haga que se pierda la selección).

Para lograr esto hay que considerar tres eventos del DataGridView: CellMouseDown, CellMouseUp y SelectionChanged.
El código es:

        int selectedIndex;

        private void dGridView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (d
GridView.Rows.Count > 0 && dGridView.SelectedRows.Count >= 1)
                selectedIndex = d
GridView.SelectedRows[0].Index;
        }

        private void d
GridView_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (d
GridView.Rows.Count > 0 && dGridView.SelectedRows.Count >= 1)
                selectedIndex = d
GridView.SelectedRows[0].Index;
        }


        private void dGridView_SelectionChanged(object sender, EventArgs e)
        {
            if (d
GridView.Rows.Count > 0 && dGridView.SelectedRows.Count >= 1)
                selectedIndex = d
GridView.SelectedRows[0].Index;
        }

Para restablecer la fila seleccionada se debe poner el siguiente código en donde se cambia la propiedad Multiselect de dGridView, ó después de cualquier código que haga que se pierda la selección:
        dGridView.Rows[selectedIndex].Selected = true;

jueves, 18 de abril de 2013

Calcular los n primeros elementos del Conjunto de Wirth

Me encontré con este problema revisando la página de PuntoPeek. Aquí copio la definición del Conjunto de Wirth:
"El conjunto de Wirth es un subconjunto de los enteros positivos que cumple con la siguiente regla: el 1 pertenece al conjunto y si un numero k pertenece al conjunto entonces los numeros 2*k+1 y 3*k+1 pertenecen al conjunto."

En un primer vistazo parecía un problema que podría resolverse con un bucle, pero en realidad es bastante más complicado:
Empiezo con 1 y calculo los siguientes dos números: 2*1+1=3 y 3*1+1=4.
Mi conjunto por ahora es: [1, 3, 4]
Los siguientes dos números son: 3*2+1=7 y 3*3+1=10
Los otros dos siguientes son: 4*2+1=9 y 4*3+1=13
Mi conjunto ahora es: [1, 3, 4, 7, 10, 9, 13]
Calculo los dos siguientes: 7*2+1=15 y 7*3+1=22
Mi conjunto ahora es: [1, 3, 4, 7, 10, 9, 13, 15, 22]
Calculo los dos siguientes: 10*2+1=21 y 10*3+1=31
Mi conjunto ahora es: [1, 3, 4, 7, 10, 9, 13, 15, 22, 21, 31]
Calculo los dos siguientes: 9*2+1=19 y 9*3+1=28
Mi conjunto ahora es: [1, 3, 4, 7, 10, 9, 13, 15, 22, 21, 31, 19, 28]

Se puede notar que los números del conjunto de Wirth no se generan de modo ascendente, si no que el conjunto se va "rellenando" a medida que se hacen más y más cálculos. Un simple bucle que calcule los 5 primeros números del conjunto de Wirth nos dará [1, 3, 4, 7, 10] cuando la respuesta correcta es [1, 3, 4, 7, 9]. El error se hace más grande con cantidades mayores.
Otro problema es que se generan números repetidos, por ejemplo en el caso de 10*3+1 y 15*2+1.
Considerando esto, se puede comprobar que varias de la respuestas dadas en esta web son erróneas.

Para evitar el caso de números faltantes, mi algoritmo calcula el doble de los números requeridos, además de comprobar que no estén repetidos, ordena el array resultante y sólo muestra la cantidad de números solicitada.

Este es el código en Python:

def Wirth(i, n, w, k):
    if n == 1:
        return 0
    else:       
        k = w[i]
        t = 2*k + 1

        if t not in w:
            w.append(t)

        t = 3*k + 1

        if t not in w:
            w.append(t)

        return Wirth(i+1, n-1, w, k)

w=[1]
n=50

Wirth(0, n*2, w, 1)
w.sort()
w = w[0 : n]
print w




Y en C# usando Linq:

 class Program
    {
        static int Wirth(int i, int n, ref int[] w, int k, int count)
        {
            if (n == 0 || count >= w.Length)
                return 0;
            else
            {
                k = w[i];
                int t = 2 * k + 1;

                if (!w.Any(c => c == t))
                {
                    count++;
                    w[count] = t;
                }

                t = 3 * k + 1;

                if (!w.Any(c => c == t))
                {
                    count++;
                    w[count] = t;
                }
              
                return Wirth(i + 1, n - 1, ref w, k, count);
            }
        }

        static void Main(string[] args)
        {         
            int n = 50;

            int[] w = Enumerable.Repeat(0, n * 2).ToArray();
            w[0] = 1;

            Wirth(0, n, ref w, 1, 0);

            w = w.Where(c => c != 0).OrderBy(c => c).ToArray();

            for (int i = 0; i < n; i++)
                Console.WriteLine(w[i]);

            Console.Read();
        }
    }


Y obtengo la misma respuesta que PuntoPeek, cuya solución me ha gustado, es bastante elegante :D

jueves, 11 de abril de 2013

VB6: Cómo saber si existe una webcam conectada a la PC

Primero declaramos las funciones SendMessage y capCreateCaptureWindow (si se declaran en un módulo .bas las declaraciones deben ser públicas, para este ejemplo asumiremos que todo el código está dentro de un Form):

Private Declare Function capCreateCaptureWindow Lib "avicap32.dll" _
    Alias "capCreateCaptureWindowA" ( _
    ByVal lpszWindowName As String, _
    ByVal dwStyle As Long, _
    ByVal x As Long, _
    ByVal y As Long, _
    ByVal nWidth As Long, _
    ByVal nHeight As Long, _
    ByVal hwndParent As Long, _
    ByVal nID As Long) As Long

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
    ByVal hwnd As Long, _
    ByVal wMsg As Long, _
    ByVal wParam As Long, _
    lParam As Any) As Long


Private Const CONNECT As Long = 1034


El código que nos dice si hay una webcam conectada es:

Dim mCapHwnd as Long

mCapHwnd = capCreateCaptureWindowA("WebCap", 0, 0, 0, 320, 240, Me.hwnd, 0)
DoEvents

If SendMessage(mCapHwnd, CONNECT, 0, 0) <> 0 Then
     
MsgBox "Se ha detectado una webcam conectada y lista para usarse"
Else
      MsgBox "No se ha detectado una webcam"
End If



Basado en este código de Recursos Visual Basic

miércoles, 27 de marzo de 2013

Code Snippet: Pequeño ejemplo del uso de Linq.Any

Definición de Linq.Any extraído de esta web:
"El operador Any se utiliza para determinar si existe un elemento dentro de una secuencia que cumpla una condición expecífica."

Pero lo que yo necesitaba era un ejemplo concreto para saber si un elemento no existe en una colección usando Linq.Any.
El siguiente código muestra cómo usar Linq.Any para saber si un elemento existe o no en una colección. Es autoexplicativo:

using System;
using System.Linq;
using System.Collections.Generic;

namespace LinqPractice
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable mis_nums = Enumerable.Range(0, 4);
            mis_nums = mis_nums.Select(c => c * 3);

            // mis_nums = {0, 3, 6, 9}

            if (mis_nums.Any(c => c != 7))
                Console.WriteLine("Existe al menos un número que es diferente de 7");

            if (mis_nums.Any(c => c == 9))
                Console.WriteLine("El 9 sí existe en la colección");
            else
                Console.WriteLine("El 9 no existe en la colección");

            if (!mis_nums.Any(c => c == 7))
                Console.WriteLine("El 7 no existe en la colección");
            else
                Console.WriteLine("El 7 sí existe en la colección");
          
            Console.Read();
        }
    }
}


El resultado es:

 

lunes, 11 de marzo de 2013

Visual C++: Cómo hacer que una ventana aparezca maximizada al cargarla

Para una aplicación tipo Win32 se debe buscar en la función Initinstance la línea donde dice ShowWindow:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   hInst = hInstance; // Almacenar identificador de instancia en una variable global

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)  
      return FALSE;   


  // Visual C++ nos crea esta línea:
  // ShowWindow(hWnd, nCmdShow);

  // debemos reemplazarla por esta otra línea:
   ShowWindow(hWnd, SW_MAXIMIZE);
   UpdateWindow(hWnd);
   return TRUE;
}


Los valores del parámetro que se le pasa a la api ShowWindow están declarados en WinUser.h y son los siguientes:

#define SW_HIDE             0
#define SW_SHOWNORMAL       1
#define SW_NORMAL           1
#define SW_SHOWMINIMIZED    2
#define SW_SHOWMAXIMIZED    3
#define SW_MAXIMIZE         3
#define SW_SHOWNOACTIVATE   4
#define SW_SHOW             5
#define SW_MINIMIZE         6
#define SW_SHOWMINNOACTIVE  7
#define SW_SHOWNA           8
#define SW_RESTORE          9
#define SW_SHOWDEFAULT      10
#define SW_FORCEMINIMIZE    11
#define SW_MAX              11


viernes, 22 de febrero de 2013

Algoritmo para obtener números primos de una forma funcional

Todo empezó cuando me puse a aprender Python (debido a que este curso requiere programar en ese lenguaje) utilizando Python Tools for Visual Studio. Primero debe bajarse e instalarse el Interpreter y, si no se tiene el Visual Studio, o se tiene la versión Express, hay que instalar The Visual Studio 2010 Shell Redistributable Package, y al final instalar Python Tools. Como resultado, tenemos un Python Express :D

Buscando tutoriales. hallé que en esta web proponían el siguiente método para obtener números primos (llamado "El Colador de Eratósthenes", no es el método más eficiente):

nums = range(2, 50)
for i in range(2, 8):
     nums = filter(lambda x: x == i or x % i, nums)


Luego el autor dice: "No sé de algún lenguaje que pueda hacer lo mismo con sus características propias de una forma tan compacta y legible como en Python (excepto para lenguajes funcionales)".

¡Sorpresa! En C# sí se puede:

int[] nums = Enumerable.Range(2, 48).ToArray();
for (int i = 2; i < 8; i++)
      nums = nums.Where(c => c == i || c % i != 0).ToArray();


Sólo hay que importar los espacios de nombres System y System.Linq.
:D

lunes, 21 de enero de 2013

Tutorial: Creando una DLL en Visual C++ 2010 y Usándola en una Aplicación en C#

Electrónica a Martillazos, actualización de la sección de VB6 y C++:
Creando una DLL en Visual C++ 2010 y Usándola en una Aplicación en C#.
Link directo
Un mirror

:D

martes, 15 de enero de 2013

El Control DataGridView lanza una System.ArgumentOutOfRangeException al recorrer sus filas

Tengo un DataGridView llamado dGridVIew al cual le añado datos de la siguiente manera:

DataRow[] dRow = MetodoQueDevuelveUnArrayDeDataRows();
object[] param = new object[3];

for (int i = 0; i < dRow.Count(); i++)
      {
           param[0] = dRow[i]["Campo 1"];
           param[1] = dRow[i]["Campo 2"];
           param[2] = dRow[i]["Campo 3"];

// Evito una excepción al hacer click en las cabeceras y si alguna celda tiene valor nulo
       for (int j = 0; j < param.Length; j++)
             if (param[j] == DBNull.Value)
                 param[j] = String.Empty;

             d
GridView.Rows.Add(param);
      }

Todo bien hasta aquí. Pero sucedía que después de llamar a este código, en el control dGridView no se mostraban todas las filas añadidas, a menos que redimensionada el formulario contenedor. Peor aún, al recorrer las filas con las teclas de las flechas hacia arriba o abajo, me saltaba la siguiente excepción:

No se controló System.ArgumentOutOfRangeException
  Message=El valor de '484' no es válido para 'Value'. 'Value' debería estar entre 'minimum' y 'maximum'.
Nombre del parámetro: Value
  Source=System.Windows.Forms
  ParamName=Value
  StackTrace:
       en System.Windows.Forms.ScrollBar.set_Value(Int32 value)
       en System.Windows.Forms.DataGridView.ScrollRows(Int32 rowCount, Int32 deltaY, ScrollEventType scrollEventType)


Rebuscando en Internet, este blog me dio la solución.
Y era muy simple: Después de llenar el DataGridView se debe llamar al método PerformLayout de la siguiente manera:

dGridView.PerformLayout();

No he podido encontrar porqué esta función evita la excepción, ni siquiera en la web de Microsoft.

Update!!

El código de ejemplo aún tiene un error: el DataGridView dGridView aún puede tirar una excepción si hacemos clic a la cabecera de una columna que espera recibir datos numéricos. El código corregido es:

DataRow[] dRow = MetodoQueDevuelveUnArrayDeDataRows();
object[] param = new object[3];

// Los valores del sgte array deben coincidir con los nombres de las columnas
string[] columnas = { "Campo 1", "Campo 2", "Campo 3" };

for (int i = 0; i < dRow.Count(); i++)
      {
           param[0] = dRow[i]["Campo 1"];
           param[1] = dRow[i]["Campo 2"];
           param[2] = dRow[i]["Campo 3"];

       // Evito una excepción al hacer click en las cabeceras y si alguna celda tiene valor nulo
       for (int j = 0; j < param.Length; j++)
             if (param[j] == DBNull.Value)

             {
       // en el caso de trabajar directamente con un Datatable la sgte línea se reemplaza por: 
      //    if (unDataTable.Columns[columnas[j]].DataType == typeof(string))
                 if (dRow[i].Table.Columns[columnas[j]].DataType == typeof(string))
                      param[j] =
String.Empty;
                 else
                      param[j] = 0;
              }


             d
GridView.Rows.Add(param);
      }

dGridView.PerformLayout();

lunes, 14 de enero de 2013

C#: Propiedad Select de un Datatable. Un par de Detalles.

Tengo un Datatable llamado "DT" que contiene muchos datos organizados en dos campos: "Campo1" y Campo 2". Quiero filtrar aquellos datos que contengan valores nulos en "Campo1" y en "Campo 2".

El código fuente será:

DataRow[] dRow = DT.Select("Campo1 is Null and [Campo 2] is Null");

Para detectar campos con valores nulos, le mandamos una consulta con "is Null". Como "Campo 2" tiene un espacio en blanco, lo pongo entre corchetes.

Si tengo este método:

DataRow[] Method1(string filtro)
{
    Datatable DT = metodoQueDevuelveUnDatatable();
    return DT.Select(filtro);
}

lo llamaré así:

DataRow[] dRow = Method1("Campo1 is Null and [Campo 2] is Null");


¿Pero cómo hago si deseo que Method1 me devuelva todas las filas de DT, sin aplicar ningún filtro?

Escribo lo siguiente:

DataRow[] dRow = Method1(String.Empty);

Enviar un String.Empty a la propiedad Select de un Datatable es una forma de decirle que no queremos filtrar nada y también de eliminar cualquier filtro aplicado con anterioridad.