La Escuela del Programador

 

Usando el Namespace Microsoft.VisualBasic en C#

El Namespace Microsoft.Visual Basic contiene clases, estructuras y enumeraciones que están disponibles automáticamente cuando estamos desarrollando una aplicación en Visual Basic.Net. Por ejemplo una clase perteneciente a este namespace es la clase Interaction que contiene métodos que se utilizan para interactuar con objetos, aplicaciones y sistemas. Lo que mucha gente no sabe es que en C#, podemos utilizar tambien los tipos incluidos en este namespace. En este artículo veremos ejemplos de los métodos de la clase Interaction y los ejemplificaré en ambos lenguajes

El propósito de este artículo es evacuar algunas consultas que me han hecho acerca de como utilizar el namespace Microsoft.VisualBasic dentro de una aplicación C#. No obstante les recomiendo utilizar las funciones nativas de .Net, aún dentro de un programa VisualBasic, ya que el resultado obtenido en este caso es más eficiente, haciendo el assembly mas pequeño y a la vez de mejor rendimiento. Sin embargo, aquí les muestro a aquellos programadores que acostumbran a usar estas funciones en VB, una alternativa para utilizarlas en C#. Aunque una buena práctica es confiar directamente en los métodos de las clases .Net y no en este namespace para así lograr un código de mejor rendimiento.

 

 

 


El método IIf, el Método InputBox y el Método MsgBox

Estos tres métodos son viejos conocidos para aquellos desarrolladores que vienen de las versiones antiguas de Visual Basic, anteriores a .Net.

 

 

 

  1. Método InputBox: Muestra un mensaje en un cuadro de diálogo, espera a que el usuario escriba texto o haga clic en un botón y, a continuación, devuelve una cadena que contiene el contenido del cuadro de texto.
  2. Método IIf: Conocido como el si inmediato, devuelve uno de dos objetos, dependiendo de la evaluación de una expresión. Proporciona un homólogo para el Operador Condicional ternario : ? : de C++ o C#.
  3. Método MsgBox: Muestra un mensaje en un cuadro de diálogo, espera a que el usuario haga clic en un botón y, a continuación, devuelve un entero que indica el botón en el que el usuario ha hecho click.

Veamos un ejemplo en VB que utiliza los 3 métodos:

Module Module1
    Sub Main()
        Dim message, title, defaultValue As String
        Dim myValue As String
        message = "Entre un numero (si no entra nada se tomará 1)"
        title = "Demostración de InputBox"
        defaultValue = "1"

        myValue = InputBox(message, title, defaultValue)
        If myValue = "" Then
             myValue = defaultValue
        End If
        Dim tamaño As String = IIf(myValue < 100, "Pequeño", 
                IIf(myValue < 1000, "Mediano", "Grande"))

        MsgBox(String.Format("{0} es {1}", myValue, tamaño), 
               MsgBoxStyle.Information, "Uso de MsgBox")
    End Sub
End Module

Ahora escribamos el mismo programa en C#. Sólo necesitamos hacer una referencia al assembly Microsoft.VisualBasic.dll y agregar el using que nos permita usar el namespace Microsoft.VisualBasic

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic;
namespace Ej_IIfyMsgBoxCS
{
  class Program
  {
    static void Main(string[] args)
    {
      string message, title, defaultValue;
      string myValue;
      message = "Entre un numero (si no entra nada se tomará 1)";
      title = "Demostración de InputBox";
      defaultValue = "1";
             
      myValue = Interaction.InputBox(message, title, defaultValue);
      if (myValue == "")
          myValue = defaultValue;
      object tamaño = Interaction.IIf(Int32.Parse(myValue) < 100, 
                         "Pequeño",
           Interaction.IIf(Int32.Parse(myValue)<1000, "Mediano", 
                          "Grande"));

      Interaction.MsgBox(String.Format("{0} es {1}", myValue, tamaño),
          MsgBoxStyle.Information, "Uso de MsgBox");
    }
  }   
}

Ambos programas tienen idénticas salidas. Primero me aparece un cuadro de entrada y al escribir un número y pulsar Aceptar, se ejecuta el método IIf y devuelve el resultado de la evaluación de la condición ("x es pequeño/mediano/grande", donde x es el número entrado) dependiendo si x es menor a 100, si está entre 100 a 999 o si es mayor o igual a 1000, respectivamente. En la figura 1 vemos la salida cuando entramos 345:

Métodos InputBox, IIf y MsgBox
Figura 1- A la izquierda el cuadro de entrada que le permite al usuario entrar un número. A la derecha, al pulsar aceptar en el cuadro de la izquierda, se ejecuta el método IIF y devuelve el valor que corresponde, a través del uso de la función MsgBox.


Observe que el código es casi idéntico, excepto que cuando llamamos a los 3 métodos, debemos hacerlo escribiendo el nombre de la clase (Interaction) seguido por un punto, antes de escribir los métodos. (Recordar que los 3 métodos son definidos en la clase como métodos de clase - shared en Visual Basic o static en C#-, por lo que la forma en que se llaman en el código C# es abolutamente lógica. Lo que si cambia es la sentencia que llama al método IIf, pero esto es debido a que por defecto, VB realiza conversiones automáticas. Esto no es una buena práctica y recomiendo a todos los programadores de VB a cambiar esta conducta y hacer que el compilador de VB, se comporte como el de C#. Para esto escribimos en la primera línea del archivo Module1.vb lo siguiente:

Option Strict On

Con esto, restringimos las conversiones de tipos de datos implícitas a sólo conversiones de ampliación, impedimos el enlace en tiempo de ejecución y denegamos el uso de tipos implícitos que dan como resultado un tipo Object. Por ese motivo, al escribir esa sentencia y tratar de compilar el programa de Visual Basic, se producen dos errores, como se ve en la figura 2:

Option Strict
Figura 2- Option Strict On, obliga a que nosotros mismos convirtamos myValue de string a Integer y a que definamos tamaño como Object en lugar de definirlo como String, ya que el valor devuelto por el método IIf es un System.Object.


Para evitar estos errores, debemos cambiar la línea siguiente:

 Dim tamaño As String = IIf(myValue < 100, "Pequeño", 
                IIf(myValue < 1000, "Mediano", "Grande"))

por esta otra:

 Dim tamaño As Object = IIf(Int32.Parse(myValue) < 100, "Pequeño",
                IIf(Int32.Parse(myValue) < 1000, "Mediano", "Grande"))

que si la comparamos con la línea que usamos en el programa C#:

object tamaño = Interaction.IIf(Int32.Parse(myValue) < 100, "Pequeño",
     Interaction.IIf(Int32.Parse(myValue)<1000, "Mediano", "Grande"));

...vemos que son casi idénticas.

El código anterior, anida dos llamados al método IIf de la clase Interaction: mediante el primero evaluamos si el numero entrado es menor que 100, en cuyo caso devolvemos la cadena "Pequeño" y en caso contrario, volvemos a hacer un nuevo llamado al método IIf, pero esta vez evaluando si el número entrado es menor a 1000, en cuyo caso devolvemos la cadena "Mediano" y si no devolvemos "Grande".

 

 

 


El método CallByName

Este método de la clase Interaction, es un método shared (de clase) que ejecuta un método en un objeto, o bien establece o devuelve una propiedad en un objeto. Supongamos que en un programa, definimos una Lista de strings, llamada diasSemana intanciada a los valores abreviados de los días de la semana en español (Lun, Mar, Mie, Jue, Vie, Sab, Dom). Podemos cambiar el primer valor de la lista (item 0) por "Lunes", llamando a este método y pasándole como parámetros, el objeto del cual queremos cambiar una propiedad (diasSemana), el nombre de la propiedad a cambiar (item), el tipo de llamado (definido por la enumeración CallType, que puede ser: Method, Get o Set, en nuestro caso será el valor CallType.Set) y un arreglo opcional de parámetros que contiene los argumentos que se van a pasar a la propiedad: en nuestro caso debemos pasar el indice (0) y el nuevo valor "Lunes". Aquí, el código ejemplo, para que usted lo pruebe y vea los resultados por si mismo:

 

 

 

Module Module1
    Dim diasSemana As List(Of String) = 
          New List(Of String)() From 
          {"Lun", "Mar", "Mie", "Jue", 
            "Vie", "Sab", "Dom"}

    Sub Main()
        CallByName(diasSemana, "Item", CallType.Set, 0, "Lunes")
        ' Recorremos la lista y escribimos sus elementos:
        For Each i In diasSemana
            Console.WriteLine(i)
        Next
    End Sub
End Module

La salida del mismo se observa en la figura 3:

LLamado a CallByName para establecer el valor de una propiedad
Figura 3- LLamado a CallByName para establecer el valor de una propiedad.


El mismo programa realizado en C# es el siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic;

namespace ConsoleApplication1
{
    class Program
    {
        static List<String> diasSemana = new List<String>() 
          { "Lun", "Mar", "Mie", "Jue", "Vie", "Sab", "Dom" };
        
        static void Main(string[] args)
        {
            Interaction.CallByName(diasSemana, "Item", 
                                   CallType.Set, 0, "Lunes");
            //  Recorremos la lista y escribimos sus elementos:
            foreach (var i in diasSemana)
                Console.WriteLine(i);
        }
    }
}
Recordar que para poder usar el namespace Microsoft.VisualBasic en C#, debemos hacer una referencia al assembly Microsoft.VisualBasic.dll. Observe que el compilador C# nos obliga a declarar el campo privado diasSemana como static (de clase), que es lo lógico ya estamos usándolo en el método Main que es un método static (de clase) y todos los miembros que usamos en un método static deben ser static... ¿Entonces que sucede en VB? Realmente lo mismo, sólo que VB, no usa una clase para definir el programa, sino un Módulo y como un Módulo en VB en realidad es una clase Shared (una clase donde todos sus miembros son automáticamente de clase -shared en VB-) no hace falta declarar ningún miembro como de clase, ya que automáticamente lo serán.


Ahora cambiemos la manera que mostramos los valores de la lista diasSemana. Para ello, en el programa de VB, cambiemos el siguiente bloque:

For Each i In diasSemana
   Console.WriteLine(i)
Next

por esta sentencia:

For i = 0 To diasSemana.Count - 1
  Console.WriteLine(CallByName(diasSemana, "Item", CallType.Get, i))
Next

En este caso estamos haciendo acceso de lectura de la propiedad item a través del llamado del método CallByName. Si comparamos este llamado con el hecho anteriormente al escribir el valor "Lunes" en el primer elemento de la lista, vemos que cambian los ultimos parámetros. El tercero, es el tipo de llamado (esta vez estamos accesando una propiedad para lectura -usamos el valor Get de la enumeración CallType) y en el cuarto sólo necesitamos pasarle el índice que queremos leer, lo que en cada iteración del bucle For, será el índice correspondiente. En el código C#, cambiamos el bucle foreach:

foreach (var i in diasSemana)
  Console.WriteLine(i);

por este bucle for:

 for (int i = 0; i < diasSemana.Count;i++ )
   Console.WriteLine(Interaction.CallByName(diasSemana, 
                             "Item", CallType.Get, i));


Por ultimo, quitemos el elemento "Mar" del segundo lugar e insertemos en ese lugar la cadena "Martes", utilizando nuevamente la función CallByName:

' Llamamos al método remove para quitar el elemeno "Mar"
CallByName(diasSemana, "Remove", CallType.Method, "Mar")

' Ahora insertamos "Martes" en el segundo lugar 
' de la lista (índice 1).
CallByName(diasSemana, "Insert", CallType.Method, 1, "Martes")

Como se ve en el código de arriba, en la primera sentencia estamos llamando a través del método CalByName al método Remove de la clase List, que espera como parámetro el elemento a remover de la lista (en nuestro caso "Mar"). En la segunda sentencia llamamos al método Insert, que espera dos parámetros: el índice dónde se insertará el elemento y el valor a insertar (en nuestro caso, se insertará "Martes" en el segundo lugar de la lista diasSemana -índice 1-). Como se observa en ambas sentencias, al hacer el llamado al método CallByName, en estas oportunidades, en ambos casos le pasamos como tercer parámetro el valor Method de la enumeración CallType. Haciendo "refactorización" (refactoring) (Técnica de la ingeniería de software para reestructurar un código fuente con el objetivo de mejorar la consistencia interna y la claridad del mismo, alterando su estructura interna sin cambiar su comportamiento externo, podemos volver a escribir el código VB de la siguiente manera:

Module Module1
    Dim diasSemana As List(Of String) =
          New List(Of String)() From
          {"Lun", "Mar", "Mie", "Jue",
            "Vie", "Sab", "Dom"}

    Sub Main()
        CallByName(diasSemana, "Item", CallType.Set, 0, "Lunes")
        RecorreLista()

        ' Llamamos al método remove para quitar el elemeno "Mar"
        CallByName(diasSemana, "Remove", CallType.Method, "Mar")

        ' Ahora insertamos "Martes" en el segundo lugar 
        ' de la lista (índice 1).
        CallByName(diasSemana, "Insert", CallType.Method, 1, "Martes")

        Console.WriteLine("=========================================")
        RecorreLista()
    End Sub

    Public Sub RecorreLista()
        For i = 0 To diasSemana.Count - 1
            Console.WriteLine(CallByName(diasSemana, "Item", 
                                     CallType.Get, i))
        Next
    End Sub
End Module

La salida obtenida al ejecutar este programa es la que se muestra en la figura 4:

Refactorizando el código fuente
Figura 4- Refactorizando el código fuente.


Finalmente escribiremos este mismo programa en C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic;

namespace ConsoleApplication1
{
    class Program
    {
        static List diasSemana = new List() 
                { "Lun", "Mar", "Mie", "Jue", "Vie", "Sab", "Dom" };


        static void Main(string[] args)
        {
            Interaction.CallByName(diasSemana, "Item",
                                   CallType.Set, 0, "Lunes");
            RecorreLista();

            // Llamamos al método remove para quitar el elemeno "Mar"
            Interaction.CallByName(diasSemana, "Remove", 
                                    CallType.Method, "Mar");

            // Ahora inserta "Martes" en el segundo lugar de la lista 
            // (índice 1).
            Interaction.CallByName(diasSemana, "Insert", 
                                    CallType.Method, 1, "Martes");

            Console.WriteLine("====================================");

            RecorreLista();
        }

         public static void RecorreLista()
         {
            for (int i = 0; i < diasSemana.Count; i++)
                Console.WriteLine(Interaction.CallByName(diasSemana,
                                          "Item", CallType.Get, i));
         }
    }
}

Métodos AppActivate y Shell

Son muchos los métodos shared que contiene la clase Interaction, pero para finalizar este artículo veremos la utilización del método AppActivate y el método Shell. Existen 2 sobrecargas de este método:

  1. Interaction.AppActivate Method (Int32): recibe un argumento entero que especifica el ID asignado al proceso que se quiere activar, que se obtiene mediante el llamado al método Shell
  2. Interaction.AppActivate Method (String) recibe un String conteniendo el título en la barra de título de la aplicación que queremos activar.

He aquí el código de una aplicación que tratará de activar una instancia de Notepad que esté corriendo en la máquina local, y en caso de que no haya ninguna corriendo, ejecutará Notepad, activándola (o sea llevando el foco sobre ella)

Module Module1
    Sub Main()
        Dim notepadID As Integer
        Try
           AppActivate("Untitled - Notepad")
        Catch ex As ArgumentException
           notepadID = Shell("C:\Windows\Notepad.exe", 
                             AppWinStyle.MaximizedFocus)
           AppActivate(notepadID)
        End Try
    End Sub
End Module

Este código declara una variable local notepadID, y trata de activar una instancia de Notepad que esté ejecutándose en la máquina local, utilizando la segunda sobrecarga del método AppActivate vista mas arriba, es decir la que espera la cadena que contenga el título de la ventana (tener en cuenta que si tenemos una versión de Windows en español, el título será:"Sin título: Bloc de Notas"). En caso de no haber ninguna instancia con ese título corriendo, se producirá una excepción del tipo ArgumentException y entonces en el bloque catch, primeramente llamamos al método Shell, pasándole dos parámetros: el path de de la aplicación (generalmente será "C:\Windows\Notepad.exe" y un valor de la enumeración contenida también en el namespace Microsoft.VisualBasic, llamada AppWinStyle (en nuestro caso le pasamos el valor MaximizedFocus, para maximizar la aplicación Notepad y llevar el foco hacia ella). El método Shell, devuelve un integer que es el ID del proceso en que corre la aplicación ejecutada a través del método Shell. Luego, llamamos a la primera sobrecarga de la función y le pasamos precisamente el ID devuelto por el método Shell. Al correr la aplicación si no hay ninguna instancia de Notepad con el título pasado (en nuestro caso "Untitled - Notepad") se ejecutará una nueva instancia y aparecerá maximizada. Si nosotros ponemos otras ventanas arriba y volvemos a correr el programa, veremos que se lleva el foco a la intancia de Notepad que esté corriendo, sin inicar una nueva (o sea que la ventana maximizada de Notepad, nuevamente quedará por encima de las demás). Para terminar el artículo veamos el código C# de la aplicación anterior:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            int notepadID;
            try
            {
                Interaction.AppActivate("Untitled - Notepad");
            }
            catch (ArgumentException ex)
            {
              notepadID = Interaction.Shell(@"C:\Windows\Notepad.exe", 
                                     AppWinStyle.MaximizedFocus);
              Interaction.AppActivate(notepadID);
            }
        }
    }
}
respag
Panamá - © 2012
http://respag.net/usando-el-namespace-microsoftvisualbasic-en-c.aspx