Archivo por meses: septiembre 2020

El porqué C# siempre tendrá novedades aunque Visual Basic ya no

Pues eso… el otro día estaba leyendo un artículo de Anthony D. Green (miembro del team de Visual Basic de Microsoft del 2010 al 2018) sobre todo lo relacionado con la que se lio por el tema de que a Visual Basic no se le añadirán nuevas características, (
Future features of .NET Core that require language changes may not be supported in Visual Basic.), pero no es de eso de lo que te quiero hablar aquí. Es de lo que dice Anthony en su artículo.

Concretamente esta parte es la que me ha hecho reflexionar un poco sobre C#:

C# advances the .NET platform.
I’m not begrudging or envying them that role but recognizing that that’s what the language has always done where VB is traditionally focused on innovation in experience.
It’s not that you can’t write a library or a framework in VB (and people have) but the .NET platform is not and will never be dependent on the VB language (or F#) to make leaps.
But if C# is late developing generics, .NET 2.0 doesn’t ship.
If C# hasn’t figured out the shape and behavior of nullable value types those types can’t appear in any new APIs.
If it’s late on LINQ, .NET 3.5 doesn’t ship.
If async isn’t complete, .NET 4.5 can’t ship with new asynchronous APIs.
If C# language design can’t converge on ref returns or nullable annotations the platform teams (like mscorlib) that depend on those features literally cannot ship their platforms for anyone.
And for what it’s worth, C# is ALWAYS late on those things (a fact I learned too late) because getting it right takes all the time in the world.
Which is why it’s insane (in hindsight) to envision an “ideal” world where you couple or serialize a language which does not fulfill that mission critical role with so many downstream dependencies and its customers’ productivity with one that does and expect good things for both of them.

Extracto de: Un manual sobre por qué el sufrimiento crónico de la comunidad de VB.NET no es necesario ni una cuestión de gastos o practicidad

Es decir, C# tiene que estar siempre actualizado ya que la plataforma .NET depende de C# (o casi). El casi es porque es el lenguaje de .NET que Microsoft tiene para poder usar las novedades que se agreguen a .NET.

Y como ahora .NET ya no será una continua actualización de .NET Framework, si no de .NET Core (multiplataforma), es lógico que también deba incorporar las novedades enfocadas a ser utilizado tanto en Windows como en MacOS, Linux, iOS, Android, etcétera.

¿Debo olvidarme de Visual Basic y pasarme a C#?

Esto no significa que a Visual Basic no se le deba seguir añadiendo nuevas características en el lenguaje, que según dice Anthony en su artículo es posible hacerlo si Microsoft dedica un par de profesionales a seguir manteniendo el lenguaje, que de otra parte puede recibir ayuda de la comunidad, como ya lo ha hecho con la última incorporación al lenguaje: Comentarios permitidos en más lugares dentro de las instrucciones que precisamente la ha agregado un participante de la comunidad: Paul1956 (Paul M Cohen).

Lo que sí tendrá Visual Basic son nuevas funcionalidades en el editor de Visual Studio (ver figura 1), pero no tendrá, por ejemplo el equivalente a record (registro) o init (establecedor de solo inicialización) en las propiedades.

Figura 1. Mostrar Inline parameters (parámetros en línea)

Así que… si quieres tener un lenguaje de programación para .NET que esté siempre actualizado a las novedades de la plataforma .NET, debes decantarte por C#.

Si no te gusta programar en C# pero sí en Visual Basic, recuerda que este último se quedará estancado en la versión 16.0, mientras que C# seguirá avanzando.
Eso no significa que no puedas seguir usando Visual Basic.

Pero si eres de los que quiere usar .NET 5.0 o las versiones que sigan apareciendo (cada año habrá una nueva y por tanto tendrá cosas nuevas), recuerda que podrás hacerlo, pero con las cosas que actualmente tiene Visual Basic.

Y si eres de los que no quieren usar nada relacionado con .NET / .NET Core / .NET Standard, podrás seguir usando Visual Basic en .NET Framework 4.8 (o anteriores), aunque este último seguirá estando disponible mientras Windows exista.

Resumiendo:
No te quedarás obsoleto si sigues usando Visual Basic. Pero si quieres poder usar en tu código las novedades agregadas a .NET tendrás que plantearte el cambio a C#, aunque sea para poder crear alguna biblioteca de clases que utilice esas novedades de .NET y las pongas a disposición de tu código de Visual Basic (la cuestión es querer arreglar las cosas 😉 ).

Aprender C#

Y si quieres pasarte a C#, ya sabes que en elGuille.info (y en este blog) tienes un montón de código en C# con su equivalente en Visual Basic (o viceversa), ya que desde enero de 2001 (sí, hace ya 19 añitos de nada) todo (o prácticamente todo) lo que he ido publicando para Visual Basic (trucos, API, utilidades, WPF/XAML, etc.) está con el código para los dos lenguajes. Y, lo más importante: ¡TODO GRATIS! (aunque también puedes hacer una donación con PayPal 😉 ).

Por supuesto también puedes apuntarte a algunos de los muchos cursos que hay en las plataformas de enseñanza online y aprender (pagando) el lenguaje C#.

Cómo lo hagas, si decides hacerlo, es cuestión tuya.

Por mi parte, seguiré usando Visual Basic como lenguaje de programación principal, sin olvidarme de C# e ir aprendiendo las cosas nuevas que vayan apareciendo.
Tanto de uno como de otro lenguaje, iré publicando artículos para que te sirva de aprendizaje o para que te aclares un poco más

En un próximo artículo te explicaré algunas de las novedades de C# 9.0 como son los tipos de registro (record), inicializadores en las propiedades (init) o instrucciones de nivel superior (Top-level statements).

Nos vemos.
Guillermo

P.S.
Traducción (automatizada del párrafo en inglés):

C# avanza la plataforma .NET.
No les estoy regateando ni envidiándoles ese papel, pero reconozco que eso es lo que siempre ha hecho el lenguaje, donde VB se centra tradicionalmente en la innovación en la experiencia.
No es que no pueda escribir una biblioteca o un marco de trabajo en VB (y la gente lo ha hecho), pero la plataforma .NET no depende y nunca dependerá del lenguaje VB (o F #) para avanzar.
Pero si C# desarrolla genéricos tarde, .NET 2.0 no sale.
Si C# no ha descubierto la forma y el comportamiento de los tipos de valores que aceptan valores NULL, esos tipos no pueden aparecer en ninguna API nueva.
Si llega tarde en LINQ, .NET 3.5 no se distribuye.
Si async no está completo, .NET 4.5 no se puede enviar con nuevas API asincrónicas.
Si el diseño del lenguaje C# no puede converger en devoluciones de referencia o anotaciones que aceptan valores NULL, los equipos de la plataforma (como mscorlib) que dependen de esas características, literalmente, no pueden enviar sus plataformas a nadie.
Y por si sirve de algo, C# SIEMPRE llega tarde en esas cosas (un hecho que aprendí demasiado tarde) porque hacerlo bien lleva todo el tiempo del mundo.
Es por eso que es una locura (en retrospectiva) imaginar un mundo «ideal» en el que se acopla o serializa un lenguaje que no cumple esa función crítica con tantas dependencias posteriores y la productividad de sus clientes con uno que sí y espera cosas buenas para ambos.

Fallo en el editor de código de VB y C# en Visual Studio 2019

Pues eso… estaba yo tan tranquilo escribiendo código en un formulario de Windows Forms cuando pasé al diseñador a mirar algún evento o nombre de un control y cuando volví al código… ¡HABÍA DESAPARECIDO!

Noté que después de cambiar del diseñador al panel de código el asterisco (*) de modificado había desaparecido… Y (seguramente) la posición en el editor de código era la misma que cuando abrí el proyecto. Así que… me puse puse a buscar lo que había escrito (por si se hubiese cambiado la posición dentro del editor) y ¡el código no estaba!

Era raro… muy raro…

Menos mal que se me ocurrió darle al botón DESHACER. ¡Y el código volvió a aparecer! (Y el asterisco de modificado también).

Todo esto era en un proyecto para Visual Basic usando Visual Studio 2019 Community «normal», aunque después probé en la Preview y también ocurre.

El problema

Para un ejemplo de lo que ocurre ver el GIF animado de la figura 1 para que te hagas una idea.

Figura 1. Fallo en el editor de formularios de Visual Studio 2019 usando Visual Basic

Lo de mostrar la versión de Visual Studio es porque este GIF (y los que te voy a mostrar después) los mandé la notificación del problema a la comunidad de desarrollo (Developer Community).
Ya que ellos (después de explicarles varias veces los pasos a seguir) no consiguieron reproducirlo y en sus capturas me mandaban la versión de Visual Studio que estaban usando.

 

Nota del 18/Sep:
Dicen que lo están investigando:
This issue is currently being investigated. Our team will get back to you if either more information is needed, a workaround is available, or the issue is resolved.
A ver qué ocurre…

 

Los pasos a seguir, al menos así es como me ocurría más a menudo (aunque no siempre ocurría) son estos:

1- Con cualquier proyecto de Visual Basic (al principio pensé que solo ocurre en VB, pero después comprobé que también ocurre en C#) deja abierto el formulario en modo de diseño y la ventana de código, de forma que el código tenga el foco antes de cerrar la solución (o el Visual Studio).
2- Al abrir nuevamente Visual Studio, te mostrará la ventana de código.
3- Escribe lo que sea y pasa a la ventana del diseñador de formularios.
4- Vuelve a la ventana del editor y verás que el código antes escrito desaparece.
5- Si lo quieres recuperar, tendrás que pulsar en el botón de deshacer.
5a- En al menos una ocasión (creo que con la versión Preview y un proyecto de C# para .NET 5.0 Preview 8) mostró un aviso de que se perderán los cambios en el diseñador (o algo así, no le presté atención ya que no pensaba que iba a ocurrir esto de perder el código), pero al darle varias veces a deshacer, el código volvió.

Pruebas y más pruebas

Este problema no ocurre siempre, ese es realmente el problema.

Para comprobar los fallos (y porque los de Microsoft me pidieron que les enviara un proyecto en el que ocurriera) creé un nuevo proyecto de Windows Forms para Visual Basic con .NET Framework 4.7.2 y, tras varias pruebas, se reprodujo el problema.

También probé con proyectos de C#, tanto con la versión normal de Visual Studio como con la versión Preview. Pero no era capaz de que volviese ocurrir en C#, pero sí en VB. Así que… pensé que solo ocurría esto en los proyectos de los pobres y ya casi abandonados usuario de Visual Basic.

Pero no: El problema también ocurre con proyectos de C#.
Más sobre esto dentro de un momento.

Usar una máquina virtual en Azure preparada para Visual Studio 2019

Los de Microsoft (concretamente John Q., me preguntaron si esto mismo me ocurría en otros equipos o le ocurría a otros desarrolladores de mi equipo… ¡sic! ¡Solo tengo a un desarrollador en mi equipo! Y no, no tengo otros equipos (máquinas) en la que poder probarlo… Se me ocurrió que podría probarlo en una máquina virtual… Pero me daba pereza crear una máquina virtual, instalar el Visual Studio y probarlo, así que… se me ocurrió probarlo en una máquina virtual de Azure.

Me creé una cuenta «FREE» de Azure (la que yo tenía ya caducó hace meses) con idea de usar allí una máquina virtual, pero la pereza era la misma… tener que crear una máquina virtual y tener que instalar el Visual Studio.

Sé que hay (o al menos había) máquinas virtuales ya preparadas con Visual Studio y que Microsoft las pone a nuestra disposición, así que… me puse a buscarla y encontré esto en la documentación de Microsoft: Imágenes de Visual Studio en Azure.
Uno de los enlaces activos era este: Visual Studio 2019: versión más reciente (16.5), pero el enlace estaba roto y no mostraba nada (salvo un error 404).

Así que… usé el enlace para Azure Marketplace (en la misma página de las imágenes de Visual Studio en Azure) y también daba error 404. Pulsé en el enlace de esa misma página que indica Ir a Azure Marketplace y con un poco de paciencia la página apareció) y entré en el apartado Apps. Filtré para que me mostrase solo las de Microsoft como publisher para el sistema operativo Windows y el tipo de producto Virtual Machine Images.

Y allí estaba: Visual Studio 2019 Latest.

Figura 2. La máquina virtual de VS para Azure


Al pulsar el enlace (no me deja copiarlo) muestra una ventana pop-up con las opciones que hay para seleccionar, primero seleccioné la versión Community para Windows 10, pero no me dejó instalarla, así que… posteriormente elegí la versión Visual Studio 2019 Comunity (latest release) on Windows Server 2019 y esa sí funcionó.

Probando a repetir el problema con Visual Studio en la máquina virtual de Azure

Allí probé el proyecto que les mandé y tras no-se-cuantas-miles-de-pruebas, el problema no se repetía.

Lo más que me ocurrió fue algo que también me ha ocurrido más veces (y seguramente tendrá que estar en una nuevo problema para que lo resuelvan) y es que al pasar o mostrarse directamente la ventana de código al abrir el proyecto, esta se muestra totalmente en blanco, tal como puedes comprobar en la figura (GIF) número 3.

Figura 3. Cuando te quedas en blanco mientras escribes código… o casi 😉

Pero el fallo inicial no se reproducía en el proyecto de prueba de Visual Basic.

El problema también ocurre con proyectos de C#

Así que… me dio por crear un nuevo proyecto de C# para Windows Forms usando .NET Core 3.1, así de paso probaba qué características tiene ese tipo de proyecto, ya que con los de Visual Basic para .NET 5.0 Preview 8, son una caca… también para C#, ya que no puedes hacer casi nada en el diseñador de formularios, al menos no puedes enlazar los eventos ni editar visualmente los menús, etc., pero esa es otra historia.

Y el fallo volvió a ocurrir… ya no recuerdo si fue a la primera o a la segunda… y eso que fue ayer… la cuestión es que funcionó y yo tenía la utilidad ScreenToGif lista para capturar los movimientos.

Y los capturé, y en la figura 4 puedes ver que con un proyecto de C# también ocurre.

Figura 4. El fallo de perder lo escrito en un proyecto de C#

Fíjate en el detalle de que antes de mostrar el diseñador de formularios aparece un mensaje indicando algo como Opening the file… si eso se muestra… ¡es prácticamente seguro que el fallo va a ocurrir!

 

Y esto es todo… a ver si lo solucionan.

Lo que si es cierto es que hay más cosas que pasan, aparte de que escribas código y se pierda o se quede la ventana de código en blanco, ya que también me ocurre que estando en el diseñador de formularios no se muestra la ventana de propiedades y tengo que cambiar a otra pestaña o cerrar y abrir el formulario para que por fin se muestre la ventana de propiedades o que al estar en la ventana de propiedades, en el despegable donde indica qué control estás mostrando esté en blanco y aún así se muestren algunas propiedades… esto último me pasó en el proyecto de C# para .NET Core 3.1 en el que tenía seleccionado un botón que quería moverlo o cambiar el valor de la propiedad Anchor y al no mostrarse esa propiedad pensé que en .NET Core 3.1 no existía esa propiedad… y no, era que fallaba la ventana de propiedades.

Si te ha ocurrido algo de esto, por favor indícalo en la página esa de Developer Community en la que he publicado el fallo (la descripción del problema está en inglés), te repito el enlace para que te sea más cómodo:

When opening a project if I modify the code and then (without saving) I show the form, when returning to the code that code does not exist (any changes made are not shown).

Es un título largo, lo sé… pero… 😉

Gracias.

Nos vemos.
Guillermo

gsCompilarNET: una biblioteca de clases para compilar código de C# o VB

Pues eso… intentando convertir la utilidad de Compilar y ejecutar a .NET 5.0 (Preview 8) me topé que (si no recuerdo mal) el .NET 5.0 no ofrece las clases para compilar (Microsoft.CodeDom.Providers.DotNetCompilerPlatform), así que… me puse a buscar en la web y me topé con un código de ejemplo (en C#) de Laurent Kempé que estaba escrito usando .NET Core 3.0 Preview 2.

Me puse a copiar el código que tenía publicado en el artículo y lo modifiqué para crear un proyecto para .NET Core 3.1 que usara las clases de compilar y ejecutar (Compiler y Runner) desde otra clase estática que es la encargada de llamar compilar y ejecutar el código. Después descubrí que ese código lo tenía publicado en GitHub, pero en realidad ya lo tenía copiado/adaptado usando el código de su web.

El problema inicial con el que me encontré es que solo compilaba código de C#, así que… lo modifiqué para que también compilara código de Visual Basic.

En principio ese código solo compilaba/ejecutaba código de consola. Así que… tuve que modificarlo para que también compilara código de Windows Forms, eso sí, todo el código debía estar en un solo fichero.

La respuesta a esto último que me dio la idea de cómo solucionarlo (o casi) la encontré en la red, pero no recuerdo en qué página, solo sé que era para ejecutar código de Visual Basic contenido en una base de datos, pero en realidad no era para código de Windows Forms, si no de consola, pero eso me sirvió para (además de agregar las referencias necesarias) poder crear el fichero json que necesita dotnet para crear el tipo adecuado de aplicación o usar las bibliotecas necesarias, la verdad es que tampoco me enteré demasiado… solo que haciéndolo, funcionaba 😉

La cuestión es que conseguí hacer operativo el compilador para usar código de Visual Basic (y C#) tanto para consola como para formularios de Windows.

Este es el código (o parte) que hace que esto sea posible:

        ' para ejecutar una DLL usando dotnet, necesitamos un fichero de configuración
        Dim jsonFile = Path.ChangeExtension(outputExe, "runtimeconfig.json")
        Dim jsonText = ""
        If compiler.EsWinForm Then
            Dim version = Compiler.WindowsDesktopApp().Version
            ' Aplicación de escritorio (Windows Forms)
            ' Microsoft.WindowsDesktop.App
            ' 5.0.0-preview.8.20411.6
            jsonText = "
{
    ""runtimeOptions"": {
    ""tfm"": ""net5.0-windows"",
    ""framework"": {
        ""name"": ""Microsoft.WindowsDesktop.App"",
        ""version"": """ & version & """
    }
    }
}"
        Else
            Dim version = Compiler.NETCoreApp().Version
            ' Tipo consola
            ' Microsoft.NETCore.App
            ' 5.0.0-preview.8.20407.11
            jsonText = "
{
    ""runtimeOptions"": {
    ""tfm"": ""net5.0"",
    ""framework"": {
        ""name"": ""Microsoft.NETCore.App"",
        ""version"": """ & version & """
    }
    }
}"
        End If
        Using sw = New StreamWriter(jsonFile, False, Encoding.UTF8)
            sw.WriteLine(jsonText)
        End Using

Hoy me ha dado por convertir el código de C# (ya modificado y operativo) a Visual Basic y he creado un «repositorio» en GitHub con el código de la DLL que he creado para compilar código.

También he publicado el código de la versión de C#, ya que actualmente tengo las dos versiones sincronizadas (es decir, los cambios y mejoras que he añadido en VB los he pasado al proyecto de C# y viceversa).

Así que… si te interesa el código de esta DLL para compilar puedes descargarlo/verlo en el repositorio de GitHub para gsCompilarNET.

Después publicaré también la utilidad de .NET 5.0 para Windows Forms que utiliza esa DLL (y la de gsColorearNET).

 

Espero que te sea de utilidad. 🙂

Nos vemos.
Guillermo

P.S.
He publicado en NuGet la utilidad ya compilada por si quieres usarla en tus proyectos de Visual Studio (final o preview):
gsCompilarNET en NuGet.

P.S.2
Esta es la pagina original de Compilar y ejecutar en elGuille.info (para .NET Framework 4.7.2 usando WPF).

Indicar a Visual Studio que al copiar texto lo haga con colores más enriquecidos (el RTF)

Pues eso… Si usas el texto copiado de Visual Studio para después mostrarlo coloreado en una publicación en la WEB (un blog, etc.) este truco que te explico aquí te será de gran ayuda, ya que le indica que a demás de colorear el código «normal» (instrucciones, comentarios y cadenas entrecomilladas) también copie el color de los tipos.

Esto lo utilizo con mi utilidad de colorear el código (gsColorearCodigo), concretamente usando la opción Colorear desde RTF del menú contextual de la caja de textos donde se pega el código a colorear.

Y ( lo que decía ese párrafo anterior 🙂 ) antes, al copiar texto del editor de Visual Studio, éste me contenía los colores de los tipos de datos (verde azulado), pero últimamente no lo hacía, así que… esta mañana me puse a ver las opciones del editor de textos (realmente buscando otra cosa, todo hay que decirlo) y me topé con una opción que no era muy aclaratorio de que podía ser eso lo que lo solucionara: Usar clasificación precisa. Pero al estar justo debajo de Copiar texto enriquecido al copiar o cortar, pues… ¡tuve que probarlo! 😉

Y eso es… aquí te dejo la nota «adicional» que puse en el post anterior en el que explico sobre la nueva versión de la utilidad de colorear código.

 

Nota adicional sobre el coloreado de Visual Studio

Comenté en el párrafo anterior que Visual Studio no pasa los colores de las clases (verde azulado) y antes lo hacía. ¿Lo habrán quitado?

No, no lo han quitado.

Y es que el que lo haga o no depende de una opción en la configuración del editor de textos de Visual Studio.

Esa opción en español es: Usar clasificación precisa.
En inglés es: Use accurate classification.

Y con esos nombres… ¿Quién lo iba a saber?

Para poder cambiar (activar) esa opción lo tienes que hacer en: (ver la captura)
Herramientas>Opciones>Editor de textos>Avanzadas
En inglés sería:
Tools>Options>Text editor>Advanced

Opción para que al copiar el código en Visual Studio coloree también las definiciones de los tipos

Fíjate que también tiene que estar seleccionada la opción Copiar texto enriquecido al copiar o cortar (Copy rich text on copy/cut).

Al pegar el texto coloreado, quedaría así:

''' <summary>
''' Devuelve la versión de la DLL.
''' Si completa es True, se devuelve también el nombre de la DLL:
''' gsColorear v 1.0.7.0 (para .NET Framework 4.7.2 revisión del dd/MMM/yyyy)
''' </summary>
Public Shared Function Version(Optional completa As Boolean = False) As String
    Dim res = ""
    Dim ensamblado = System.Reflection.Assembly.GetExecutingAssembly
    'Dim m_fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(ensamblado.Location)
    'res = $"v {m_fvi.FileVersion}"

    Dim versionAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyVersionAttribute), False)
    'DirectCast(DirectCast(versionAttr, System.Reflection.AssemblyVersionAttribute())(0), System.Reflection.AssemblyVersionAt
    Dim vers = If(versionAttr.Length > 0, (TryCast(versionAttr(0), System.Reflection.AssemblyVersionAttribute)).Version,
                                      "1.0.7.0")
    Dim fileVerAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyFileVersionAttribute), False)
    Dim versF = If(fileVerAttr.Length > 0, (TryCast(fileVerAttr(0), System.Reflection.AssemblyFileVersionAttribute)).Version,
                                      "1.0.7.1")

    res = $"v {vers} ({versF})"

    If completa Then
        Dim prodAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyProductAttribute), False)
        Dim producto = If(prodAttr.Length > 0, (TryCast(prodAttr(0), System.Reflection.AssemblyProductAttribute)).Product,
                                        "gsColorear")

        res = $"{producto} {res} (para .NET Framework 4.7.2 revisión del 09/Sep/2020)"
    End If
    Return res
End Function

 

Nos vemos.
Guillermo

Utilidad para colorear el código a mostrar en páginas WEB (gsColorearCodigo)

Pues eso… si eres de los que no entran habitualmente en el sitio del Guille (www.elguille.info) y prefieres ver las cosas publicadas aquí en el blog, te diré que ayer publiqué la nueva versión de la utilidad gsColorearCodigo. Y digo NUEVA porque la actualización anterior que publiqué fue hace ya unos 13 añitos de nada 😉 y era para .NET 2.0, mientras que la nueva es para .NET Framework 4.8.

Para facilitar la «coloración» del código a publicar en la WEB esta nueva versión permite copiar el código de Visual Studio, pegarlo en la ventana de la utilidad y generar el código HTML a partir del contenido RTF (ver la figura 1).

Figura 1. La utilidad gsColorearCodigo usando Colorear desde RTF

Esto tiene dos ventajas, la primera es que no es necesario que la utilidad analice el código fuente buscando palabras clave y comentarios, etc. Ya que lo único que tiene que hacer es convertir el código interno del formato RTF y convertirlo en etiquetas span.

Además que al hacerlo así se gana tiempo… te lo digo por experiencia, ya que prácticamente todo el código coloreado que publico desde hace 12 o 13 años lo hago usando esa opción… así que… ya sabes… 😉

Y para muestra, un código coloreado, precisamente el que usé para hacer la captura de la figura 1.

Te lo vuelvo a mostrar en la captura de la figura 2, que es el editor de Visual Studio 2019.

Figura 2. El código a copiar del editor de Visual Studio 2019

Y este es el código HTML generado :

''' <summary>
''' Devuelve la versión de la DLL.
''' Si completa es True, se devuelve también el nombre de la DLL:
''' gsColorear v 1.0.7.0 (para .NET Framework 4.7.2 revisión del dd/MMM/yyyy)
''' </summary>
Public Shared Function Version(Optional completa As Boolean = False) As String
    Dim res = ""
    Dim ensamblado = System.Reflection.Assembly.GetExecutingAssembly
    'Dim m_fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(ensamblado.Location)
    'res = $"v {m_fvi.FileVersion}"

    Dim versionAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyVersionAttribute), False)
    'DirectCast(DirectCast(versionAttr, System.Reflection.AssemblyVersionAttribute())(0), System.Reflection.AssemblyVersionAt
    Dim vers = If(versionAttr.Length > 0, (TryCast(versionAttr(0), System.Reflection.AssemblyVersionAttribute)).Version,
                                      "1.0.7.0")
    Dim fileVerAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyFileVersionAttribute), False)
    Dim versF = If(fileVerAttr.Length > 0, (TryCast(fileVerAttr(0), System.Reflection.AssemblyFileVersionAttribute)).Version,
                                      "1.0.7.1")

    res = $"v {vers} ({versF})"

    If completa Then
        Dim prodAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyProductAttribute), False)
        Dim producto = If(prodAttr.Length > 0, (TryCast(prodAttr(0), System.Reflection.AssemblyProductAttribute)).Product,
                                        "gsColorear")

        res = $"{producto} {res} (para .NET Framework 4.7.2 revisión del 09/Sep/2020)"
    End If
    Return res
End Function

En este caso, el VS no ha pasado los colores de los tipos (verde azulado), pero en otras ocasiones sí que lo hacía y quedaba guay, pero últimamente no le pasa los datos del color al código que utiliza de RTF… en fin…

 

Nota adicional sobre el coloreado de Visual Studio

Comenté en el párrafo anterior que Visual Studio no pasa los colores de las clases (verde azulado) y antes lo hacía. ¿Lo habrán quitado?

No, no lo han quitado.

Y es que el que lo haga o no depende de una opción en la configuración del editor de textos de Visual Studio.

Esa opción en español es: Usar clasificación precisa.
En inglés es: Use accurate classification.

Y con esos nombres… ¿Quién lo iba a saber?

Para poder cambiar (activar) esa opción lo tienes que hacer en: (ver la captura)
Herramientas>Opciones>Editor de textos>Avanzadas
En inglés sería:
Tools>Options>Text editor>Advanced

Opción para que al copiar el código en Visual Studio coloree también las definiciones de los tipos

Fíjate que también tiene que estar seleccionada la opción Copiar texto enriquecido al copiar o cortar (Copy rich text on copy/cut).

Al pegar el texto coloreado, quedaría así:

''' <summary>
''' Devuelve la versión de la DLL.
''' Si completa es True, se devuelve también el nombre de la DLL:
''' gsColorear v 1.0.7.0 (para .NET Framework 4.7.2 revisión del dd/MMM/yyyy)
''' </summary>
Public Shared Function Version(Optional completa As Boolean = False) As String
    Dim res = ""
    Dim ensamblado = System.Reflection.Assembly.GetExecutingAssembly
    'Dim m_fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(ensamblado.Location)
    'res = $"v {m_fvi.FileVersion}"

    Dim versionAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyVersionAttribute), False)
    'DirectCast(DirectCast(versionAttr, System.Reflection.AssemblyVersionAttribute())(0), System.Reflection.AssemblyVersionAt
    Dim vers = If(versionAttr.Length > 0, (TryCast(versionAttr(0), System.Reflection.AssemblyVersionAttribute)).Version,
                                      "1.0.7.0")
    Dim fileVerAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyFileVersionAttribute), False)
    Dim versF = If(fileVerAttr.Length > 0, (TryCast(fileVerAttr(0), System.Reflection.AssemblyFileVersionAttribute)).Version,
                                      "1.0.7.1")

    res = $"v {vers} ({versF})"

    If completa Then
        Dim prodAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyProductAttribute), False)
        Dim producto = If(prodAttr.Length > 0, (TryCast(prodAttr(0), System.Reflection.AssemblyProductAttribute)).Product,
                                        "gsColorear")

        res = $"{producto} {res} (para .NET Framework 4.7.2 revisión del 09/Sep/2020)"
    End If
    Return res
End Function

 

Lo que si hace es usar la coloración de las cadenas con los argumentos de las cadenas interpoladas (las que se usan poniendo un signo dólar delante del inicio de las comillas dobles) que utiliza la coloración de Visual Studio, mientras que si lo haces con el comando Colorear en HTML, todo el contenido de la cadena estaría de color rojo, tal como puedes ver a continuación.

        res = $"{producto} {res} (para .NET Framework 4.7.2 revisión del 09/Sep/2020)"
    End If
    Return res
End Function

Aunque con la utilidad puedes configurar los colores y usar Colorear en HTML y mostrar el código con el look ese que tanto le gusta a «los otros» 😉

''' <summary>
''' Devuelve la versión de la DLL.
''' Si completa es True, se devuelve también el nombre de la DLL:
''' gsColorear v 1.0.7.0 (para .NET Framework 4.7.2 revisión del dd/MMM/yyyy)
''' </summary>
Public Shared Function Version(Optional completa As Boolean = False) As String
    Dim res = ""
    Dim ensamblado = System.Reflection.Assembly.GetExecutingAssembly
    'Dim m_fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(ensamblado.Location)
    'res = $"v {m_fvi.FileVersion}"

    Dim versionAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyVersionAttribute), False)
    'DirectCast(DirectCast(versionAttr, System.Reflection.AssemblyVersionAttribute())(0), System.Reflection.AssemblyVersionAt
    Dim vers = If(versionAttr.Length > 0, (TryCast(versionAttr(0), System.Reflection.AssemblyVersionAttribute)).Version,
                                      "1.0.7.0")
    Dim fileVerAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyFileVersionAttribute), False)
    Dim versF = If(fileVerAttr.Length > 0, (TryCast(fileVerAttr(0), System.Reflection.AssemblyFileVersionAttribute)).Version,
                                      "1.0.7.1")

    res = $"v {vers} ({versF})"

    If completa Then
        Dim prodAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyProductAttribute), False)
        Dim producto = If(prodAttr.Length > 0, (TryCast(prodAttr(0), System.Reflection.AssemblyProductAttribute)).Product,
                                        "gsColorear")

        res = $"{producto} {res} (para .NET Framework 4.7.2 revisión del 09/Sep/2020)"
    End If
    Return res
End Function

Por si te interesa, aquí tienes los colores a usar y el valor de «pre» para conseguir ese coloreado del código:

Tag del formato <pre>:
<pre style=»background-color:black; color:#dcdcdc»>
Color texto: d69d85
Color comentario: 57a64a
Color documentación (XML): 57a64a
Color instrucciones: 569cd6

Bueno, no te canso más… espero que te sea de utilidad y la uses si necesitas colorear código para publicar en una sitio web.

El código fuente y el instalador de ClickOnce lo encontrarás en la página que publiqué en elGuille.info.

Este es el enlace para instalar la utilidad con ClickOnce y este otro es para que veas la página de la utilidad gsColorearCodigo en elguille.info.

 

¡Hasta pronto y que lo colorees bien! 🙂

 

Nos vemos.
Guillermo

P.S.
Ahora que veo el código publicado… no queda mal el fondo negro con el resto de colores… lo mismo modifico la utilidad de colorear para que al colorear desde RTF utilice esos colores en vez de los «predeterminados», que yo creo que son los que recoge del código RTF de Visual Studio, pero lo mismo es fácil de cambiarlos.
Si lo logro, ya te enterarás con una nueva actualización 😉

Compilar código en .NET Core (.NET 5.0) para consola y Windows Forms

Pues eso… usando la aplicación que te mostré el otro día (Compilar y ejecutar versión para .NET Core (.NET 5.0)) y después de hacer varias pruebas con otra DLL que compila el código y lo ejecuta sin usar Process.Start, al menos si es una aplicación de consola, (ya que para ejecutar las de Windows Forms, al menos hasta lo que yo he probado, solo puedo lanzarla usando Process.Start); al final me he quedado con la llamada a dotnet para crear el proyecto, compilarlo y ejecutar el código compilado.

Nota:
Abajo te dejo un ZIP con el código fuente del proyecto (Compilar y ejecutar) y la utilidad para colorear (gsColorearCore).
En la carpeta pruebas del proyecto está el código fuente (de VB y C#) de la aplicación de ejemplo.

Compilar con dotnet (desde la línea de comandos)

En las primeras pruebas (las del código que te puse en el post arriba mencionado, usaba aplicaciones de consola, más que nada porque así se lo decía al usar el comando:

dotnet new console -o "AppDir" -lang c#|vb

Ese código lo que hace es crear una aplicación de consola en el directorio indicado por AppDir (si ya existe, podemos añadir –force al final para que genere el contenido aunque sobreesciba lo que ya hubiese.).

Para crear/compilar una aplicación para Windows Forms tendremos que usar una línea de comandos parecida a la anterior, pero indicando winforms en vez de console.
En el siguiente código creamos una aplicación de .NET 5.0 Core usando Visual Basic en el directorio E:\Guille\source\repos\MiAppWinF.

Nota:
Si no tienes instalado el .NET 5.0 y tienes al menos el .NET Core 3.0 o 3.1 simplemente usa el lenguaje C# en lugar de Visual Basic, ya que en esas versiones anteriores al 5.0 solo se permiten aplicaciones de Windows Forms o WPF para C#.

dotnet new winforms -o "E:\Guille\source\repos\MiAppWinF" -lang vb

Nota:
En realidad poner el directorio entre comillas dobles no es necesario, salvo que se utilice el comando dotnet build.

Una vez hecho esto, solo tendremos que modificar el código generado y si queremos que se ejecute el contenido de ese directorio podemos usa la siguiente línea de comandos:

dotnet run -p E:\Guille\source\repos\MiAppWinF

El código usado en la aplicación Compilar y ejecutar NETCore

En el código de la aplicación compilar y ejecutar para .NET Core 5.0 este último paso me lo salto y lo que hago es crear la aplicación (con –force) sustituyo el código. ya que –force o simplemente al crear con new se generan los ficheros en blanco (sin el código que ya tuviera).
En su lugar utilizo build para compilar el código.
Es decir, creo la aplicación con new (uso –force por si ya existiera), guardo el código fuente del editor del programa, lo compilo con build y finalmente lo ejecuto usando Process.Start.

El comando build usado es el siguiente:

dotnet build "E:\Guille\source\repos\MiAppWinF"

Las comillas dobles solo son necesarias si el path o el nombre del proyecto contiene espacios.

Y ese comando habrá creado un ejecutable (aparte de la DLL que siempre genera .NET Core) en el directorio: E:\Guille\source\repos\MiAppWinF\bin\Debug\net5.0-windows cuyo nombre será MiAppWinF.exe
Fíjate que al ser una aplicación para Windows (las aplicaciones de Windows Forms y las de WPF solo se pueden usar en plataformas Windows, es decir, no se pueden usar ni en Linux ni en MacOS) se crea en un directorio aparte de las aplicaciones de consola, que sería en: bin\Debug\net5.0\

Nota:
El directorio de salida puede ser diferente si antes de build (y después de new) cambias la configuración del proyecto.

Un poco de código fuente por favor

El código del método compilar al que se le pasa el código a compilar/ejecutar.

''' <summary>
''' Compilar el código indicado en el parámetro.
''' 
''' Usando dotnet (.NET Core) se hará lo siguiente:
''' Definir un directorio para VB o C#: MiApp_VB o MiApp_CS
''' Usar el comando:
''' dotnet new console -o dir -lang C#|VB --force
''' Copiar en ese directorio el fichero como Program.vb o .cs
''' Usar el comando (y redirigir la salida):
''' dotnet run -p dir
''' </summary>
Private Sub compilar(texto As String, mostrarSoloSalida As Boolean)
    ' Compilar usando la línea de comandos
    Dim ext = If(optCS.IsChecked, "cs", "vb")

    Dim appDir = System.IO.Path.Combine(System.Environment.CurrentDirectory,
                                        $"MiApp_{ext}")

    ' crear un fichero temporal para compilar
    Dim ficProgram = Path.Combine(appDir, $"Program.{ext}")

    Dim dotnet = "dotnet"
    Dim args = "--version"
    txtSalida.Text = $"{dotnet} {args} = {ejecutar(dotnet, args, waitSecs:=5000)}"

    ' Considero que es aplicación de Windows Forms              (07/Sep/20)
    ' si contiene InitializeComponent
    Dim esWF = texto.IndexOf("InitializeComponent()") > -1
    If esWF Then
        args = $"new winforms -o ""{appDir}"" -lang {If(optCS.IsChecked, "c#", "vb")} --force"
    Else
        args = $"new console -o ""{appDir}"" -lang {If(optCS.IsChecked, "c#", "vb")} --force"
    End If

    If mostrarSoloSalida Then
        ejecutar(dotnet, args, waitSecs:=10000)
    Else
        txtSalida.Text &= $"{vbCrLf}{vbCrLf}{ejecutar(dotnet, args, waitSecs:=10000)}"
    End If

    ' Eliminar todos los ficheros del lenguaje
    ' (solo quedará el que se guarde)
    Dim files = Directory.GetFiles(appDir, $"*.{ext}")
    For i = 0 To files.Length - 1
        File.Delete(files(i))
    Next

    ' Guardar el código a compilar
    Using sw As New System.IO.StreamWriter(ficProgram,
                                           False,
                                           System.Text.Encoding.UTF8)
        sw.Write(texto)
    End Using

    args = $"build ""{appDir}"""

    If mostrarSoloSalida Then
        Dim res = ejecutar(dotnet, args, waitSecs:=10000)
        Dim hayErrorComp = False
        If optVB.IsChecked Then
            If res.Contains("error BC") Then
                hayErrorComp = True
            End If
        Else
            If res.Contains("error CS") Then
                hayErrorComp = True
            End If
        End If
        If hayErrorComp Then
            txtSalida.Text &= vbCrLf & res
            txtSalida.Text &= $"{vbCrLf}--- FIN DE LA COMPILACIÓN CON ERRORES ---"
            Return
        End If
        'txtSalida.Text &= $"{vbCrLf}{vbCrLf}{res}"
    Else
        txtSalida.Text &= $"{vbCrLf}{vbCrLf}{ejecutar(dotnet, args, waitSecs:=10000)}"
    End If

    Dim exe = System.IO.Path.Combine(appDir, $"bin\Debug\net5.0\MiApp_{ext}.exe")
    ' Si es aplicación de Windows Forms, usa otro directorio    (07/Sep/20)
    If esWF Then
        'net5.0-windows
        exe = System.IO.Path.Combine(appDir, $"bin\Debug\net5.0-windows\MiApp_{ext}.exe")
        txtSalida.Text &= $"{vbCrLf}{vbCrLf}Aplicación de Windows"
    End If
    txtSalida.Text &= $"{vbCrLf}{vbCrLf}{ejecutar(exe, esWinF:=esWF)}"

    txtSalida.Text &= $"{vbCrLf}--- FIN DE LA COMPILACIÓN ---"

    txtSalida.SelectionStart = txtSalida.Text.Length
    txtSalida.SelectionLength = 0
End Sub

Del método ejecutar tengo dos versiones, la primera es la que uso para llamar al dotnet y en el que redirijo la salida de la consola para capturarla y mostrarla en la caja de textos txtSalida.
Al primero le paso el nombre de la aplicación a ejecutar (dotnet) y los argumentos.
Al segundo le paso el nombre del ejecutable y si es o no aplicación de Windows, en cuyo caso se usa ShellExecute y el estilo de la ventana es normal, aparte de no usar Kill para que no se cierre la aplicación al poco de haberse abierto 🙂

Private Function ejecutar(exe As String, arg As String,
                          Optional conKill As Boolean = False,
                          Optional waitSecs As Integer = 10000) As String
    Dim res = ""
    Using p As New Process
        p.StartInfo.FileName = exe
        p.StartInfo.Arguments = arg

        ' Indicamos que queremos redirigir la salida
        p.StartInfo.RedirectStandardOutput = True
        ' Para redirigir la salida, UseShellExecute debe ser falso
        p.StartInfo.UseShellExecute = False

        p.StartInfo.CreateNoWindow = True

        Try
            ' Iniciamos el proceso
            p.Start()

            ' Esperar a que el proceso finalice
            '
            ' Esperamos waitSecs segundos para que le de tiempo a ejecutarse
            ' como mínimo esperar 2000 (o 5000 si se usa dotnet)
            If waitSecs < 2000 Then waitSecs = 2000
            p.WaitForExit(waitSecs)

            If conKill Then
                p.Kill()
            End If

            res = p.StandardOutput.ReadToEnd()
        Catch ex As Exception

            res = ex.Message
        End Try
    End Using

    Return res
End Function

''' <summary>
''' Ejecutar el código y mostrarlo en la ventana de salida.
''' </summary>
Private Function ejecutar(exe As String,
                          Optional waitSecs As Integer = 2000,
                          Optional esWinF As Boolean = False) As String
    Dim p As New Process

    p.StartInfo.FileName = exe

    If esWinF Then

        With p.StartInfo
            .UseShellExecute = True
            .WindowStyle = ProcessWindowStyle.Normal
        End With

        ' Iniciamos el proceso
        p.Start()

        ' Esperar a que el proceso finalice
        '
        ' Esperamos 2 segundos para que le de tiempo a ejecutarse
        ' Como mínimo 2 segundos
        If waitSecs < 2000 Then waitSecs = 2000
        p.WaitForExit(waitSecs)

        Return ""
    End If

    ' Indicamos que queremos redirigir la salida
    p.StartInfo.RedirectStandardOutput = True
    ' Para redirigir la salida, UseShellExecute debe ser falso
    p.StartInfo.UseShellExecute = False

    ' No usar esto
    ' salvo que se use p.Kill()
    p.StartInfo.CreateNoWindow = True

    ' Iniciamos el proceso
    p.Start()

    ' Esperar a que el proceso finalice
    '
    ' Esperamos 2 segundos para que le de tiempo a ejecutarse
    ' Como mínimo 2 segundos
    If waitSecs < 2000 Then waitSecs = 2000
    p.WaitForExit(waitSecs)
    Try
        p.Kill()
    Catch ex As Exception
        Debug.WriteLine(ex.Message)
    End Try

    ' Convertir la salida usando el código de página 437
    ' que es la usada en MS-DOS (línea de comandos)
    Dim res = p.StandardOutput.ReadToEnd()

    Return res
End Function

Recomendación para usar código de Windows Forms en la utilidad de Compilar y ejecutar

Por último, comentarte que si quieres compilar una app para Windows Forms desde la utilidad de compilar y ejecutar, el código de esa aplicación debe estar en un solo fichero, en el que te recomiendo que incluyas el método Main, la definición del diseñador de Windows Forms y el código que realmente quieres usar.
Esto es así porque el programa/utilidad no está preparado para usar múltiples ficheros.

Te muestro a continuación cómo sería ese código para Visual Basic y para C# y una captura de la salida realizada al compilar desde la utilidad.

Este es el código de Visual Basic del formulario de pruebas.

Imports System
Imports System.Windows.Forms

'
' El punto de entrada del programa
'

Friend Module Program

    <STAThread()>
    Friend Sub Main(args As String())
        Application.SetHighDpiMode(HighDpiMode.SystemAware)
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(New Form1)
    End Sub

End Module

'
' El diseñador del formulario
'

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.TextBox1 = New System.Windows.Forms.TextBox()
        Me.Button1 = New System.Windows.Forms.Button()
        Me.Label1 = New System.Windows.Forms.Label()
        Me.Button2 = New System.Windows.Forms.Button()
        Me.SuspendLayout()
        '
        'TextBox1
        '
        Me.TextBox1.Location = New System.Drawing.Point(12, 12)
        Me.TextBox1.Name = "TextBox1"
        Me.TextBox1.Size = New System.Drawing.Size(182, 23)
        Me.TextBox1.TabIndex = 0
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(200, 11)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(75, 23)
        Me.Button1.TabIndex = 1
        Me.Button1.Text = "Button1"
        Me.Button1.UseVisualStyleBackColor = True
        '
        'Label1
        '
        Me.Label1.Location = New System.Drawing.Point(12, 38)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(263, 23)
        Me.Label1.TabIndex = 2
        Me.Label1.Text = "Label1"
        '
        'Button2
        '
        Me.Button2.Location = New System.Drawing.Point(200, 90)
        Me.Button2.Name = "Button2"
        Me.Button2.Size = New System.Drawing.Size(75, 23)
        Me.Button2.TabIndex = 3
        Me.Button2.Text = "Cerrar"
        Me.Button2.UseVisualStyleBackColor = True
        '
        'Form1
        '
        Me.AcceptButton = Me.Button1
        Me.AutoScaleDimensions = New System.Drawing.SizeF(7.0!, 15.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.CancelButton = Me.Button2
        Me.ClientSize = New System.Drawing.Size(287, 125)
        Me.Controls.Add(Me.Button2)
        Me.Controls.Add(Me.Label1)
        Me.Controls.Add(Me.Button1)
        Me.Controls.Add(Me.TextBox1)
        Me.Name = "Form1"
        Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
        Me.Text = "Form1"
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub

    Friend WithEvents TextBox1 As TextBox
    Friend WithEvents Button1 As Button
    Friend Label1 As Label
    Friend Button2 As Button
End Class

'
' El código del formulario
'

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        TextBox1.Text = "<tu nombre>"

        'AddHandler Button2.Click, AddressOf Button2_Click
        AddHandler Button2.Click, Sub() Me.Close()
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim str = TextBox1.Text
        If str = "<tu nombre>" Then
            str = " amigo"
        End If
        Label1.Text = $"¡Hola {ToUpperFirst(str)}!"
    End Sub

    ''' <summary>
    ''' Convierte en mayúsculas el primer carácter de la cadena indicada.
    ''' </summary>
    Private Function ToUpperFirst(str As String) As String
        'Return str(0).ToString().ToUpper() & str.Substring(1)
        if str="" then str=" amigo"
        Return str(0).ToString.ToUpper & str.Substring(1)
    End Function

    'Private Sub Button2_Click(sender As Object, e As EventArgs)
    '    Me.Close()
    'End Sub
End Class

Este es el código de C# del formulario de pruebas.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;

//
// El punto de entrada del programa
//

static class Program
{
    /// <summary>
    ///  The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}


//
// El diseñador del formulario
//

partial class Form1
{
    /// <summary>
    ///  Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    ///  Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    ///  Required method for Designer support - do not modify
    ///  the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.textBox1 = new System.Windows.Forms.TextBox();
        this.button1 = new System.Windows.Forms.Button();
        this.label1 = new System.Windows.Forms.Label();
        this.button2 = new System.Windows.Forms.Button();
        this.SuspendLayout();
        // 
        // textBox1
        // 
        this.textBox1.Location = new System.Drawing.Point(12, 12);
        this.textBox1.Name = "textBox1";
        this.textBox1.Size = new System.Drawing.Size(182, 23);
        this.textBox1.TabIndex = 0;
        // 
        // button1
        // 
        this.button1.Location = new System.Drawing.Point(200, 12);
        this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(75, 23);
        this.button1.TabIndex = 1;
        this.button1.Text = "button1";
        this.button1.UseVisualStyleBackColor = true;
        this.button1.Click += new System.EventHandler(this.button1_Click);
        // 
        // label1
        // 
        this.label1.Location = new System.Drawing.Point(12, 38);
        this.label1.Name = "label1";
        this.label1.Size = new System.Drawing.Size(263, 23);
        this.label1.TabIndex = 2;
        this.label1.Text = "label1";
        // 
        // button2
        // 
        this.button2.Location = new System.Drawing.Point(200, 90);
        this.button2.Name = "button2";
        this.button2.Size = new System.Drawing.Size(75, 23);
        this.button2.TabIndex = 3;
        this.button2.Text = "Cerrar";
        this.button2.UseVisualStyleBackColor = true;
        //this.button2.Click += new System.EventHandler(this.button1_Click);
        // 
        // Form1
        // 
        this.AcceptButton = this.button1;
        this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.CancelButton = this.button2;
        this.ClientSize = new System.Drawing.Size(287, 125);
        this.Controls.Add(this.label1);
        this.Controls.Add(this.button1);
        this.Controls.Add(this.textBox1);
        this.Controls.Add(this.button2);
        this.Name = "Form1";
        this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
        this.Text = "Form1";
        this.Load += new System.EventHandler(this.Form1_Load);
        this.ResumeLayout(false);
        this.PerformLayout();

    }

    #endregion

    private System.Windows.Forms.TextBox textBox1;
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.Label label1;
    private System.Windows.Forms.Button button2;
}

//
// El código del formulario
//

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        textBox1.Text = "<tu nombre>";
        
        //button2.Click += => (object o, EventArgs e) this.Close();
        //button2.Click += delegate (object sender, EventArgs e) { this.Close(); };
        button2.Click += (sender, e) => { this.Close(); };
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //label1.Text = $"Hola {ToUpperFirst(textBox1.Text)}!";
        var str = textBox1.Text;
        if( str == "<tu nombre>" ) str = " amigo";
        label1.Text = $"Hola {ToUpperFirst(str)}!";
    }

    /// <summary>
    /// Convierte en mayúsculas el primer carácter de la cadena indicada.
    /// </summary>
    private string ToUpperFirst(string str)
    {
        if(str=="") str = " amigo";
        return str[0].ToString().ToUpper() + str.Substring(1);
    }
}

Esta es la salida del código ejecutándose con la utilidad de compilar y ejecutar para NETCore.

Figura 1. El resultado de compilar y ejecutar una aplicación de Windows Forms para .NET Core 5.0.

Y esto es todo… tengo algo más por ahí, para seguir con la compilación y ejecución del código para .NET Core, pero eso será para otra ocasión 🙂

 

Espero que te haya sido de utilidad… Esa es la idea y ¡eso espero! 🙂

 

Nos vemos.
Guillermo

El ZIP con el código fuente:

ZIP: Compilar_ejecutar_NetCore_20200908_1551.zip (71.8 KB)
MD5 checksum: 52A9AD9A1F1605547D1C8451CADD4CF0

Compilar y ejecutar versión para .NET Core (.NET 5.0)

Pues eso… siguiendo las pruebas de crear proyectos de .NET Core (con .NET 5.0) para Visual Basic, aquí traigo el de Compilar y Ejecutar que está creado con WPF / XAML y que además usa la biblioteca de clases para colorear (gsColorearCore) que también he convertido para .NET Core, en esta ocasión para la versión 3.1.

La he convertido para usar con .NET 5.0 (.NET Core), más que nada para probar, pero visto lo visto (los problemas) prefiero quedarme con la versión de .NET Framework.

Nota:
Abajo tienes una actualización del 16/Sep/2020

¿Por qué es preferible usar .NET Framework para este tipo de aplicación?

Por la sencilla razón de que salvo cosas puntuales, no tiene mucho sentido hacerla para .NET Core. Ya que este tipo de aplicaciones (las creadas para WPF o Windows Forms) solo se ejecutarán o funcionarán en equipos que utilicen Windows como sistema operativo.

Que tú prefieres usar el .NET Core porque «DICEN» que es más rápido, ocupa menos, se puede «embeber» con la aplicación y otras monerías… pues muy bien… lo mismo yo utilizo esas cosas «puntuales» para hacer algo con .NET 5.0, o hacer mucho cuando ya sea .NET 6.0, el tiempo lo dirá… No hay que descartar nada ni decir de este agua no beberé 😉

Cosas a tener en cuenta en la migración de .NET Framework a .NET Core

Aparte de que hay ciertas cosas que ya no existen, al menos de la forma a la que estamos habituados en las aplicaciones para .NET Framework, el resto no cambia nada o no cambia mucho.

Configuración (My.Settings)

Por ejemplo, me he encontrado con problemas a la hora de usar My.Settings. Me estuvo funcionando y de buenas a primeras dejó de hacerlo, así que… corté por lo sano y me deshice de My.Settings y las configuración la uso por mi cuenta, concretamente usando la clase Config que tengo definida en la DLL de gsColorear.

No quiero decir con esto que no se puedan usar… solo digo que a mí me estuvo funcionando bien hasta que de buenas a primeras dejó de funcionar.

Propiedades del proyecto

A las propiedades del proyecto ya no accede desde el nodo MyProject.
Ahora puedes hacerlo pulsando en el nombre del proyecto con el botón secundario del ratón (normalmente el derecho) y del menú mostrado pulsar en Propiedades (ver la figura 1).

Figura 1. Acceder a las propiedades del proyecto

NOTA:
Si simplemente haces clic en el nombre del proyecto (y la ventana de propiedades no estaba abierta anteriormente y ocasionalmente si también está abierta) se mostrará el fichero .vbproj con las opciones de configuración en formato XML no con tantas «cosas» como el de una aplicación para .NET Framework (ver la figura 2).
Ahí, entre otras cosas, se indica la versión del .NET Core que estás usando (en este caso .NET 5.0 para Windows), qué tipo de aplicación es, el espacio de nombres, etc.

Figura 2. Propiedades del proyecto en XML

Este es el aspecto (simplificado) de la ventana de propiedades del proyecto (ver figura 3).

Figura 3. Ventana de propiedades del proyecto

Si pulsas en Paquete (en el panel izquierdo de la ventana de Propiedades) tendrás algo parecido a la información del ensamblado de las aplicaciones para .NET Framework.

Referencias

Las referencias a DLL externas (u otros proyectos), se pueden hacer desde la ventana de propiedades, pero en la ventana del explorador de soluciones ya no se muestra como el nodo Referencias. Ahora está en Dependencias.

Si pulsas en Referencias de la ventana de propiedades, verás que está vacía, pero puedes agregar las referencias que necesites, supongo que, a diferencia de una aplicación de .NET Framework, aquí solo tienes que añadir las referencias externas, es decir, las que no se encuentren ya definidas en el propio .NET Core.

En realidad en Dependencias añadirás las mismas cosas que antes (con .NET Framework) añadías en tu proyecto: paquetes NuGet, referencias a otros proyectos, etc.

En el caso de las referencias a otros proyecto, están en un nodo diferenciado (Proyectos), tal como puedes comprobar en la figura 4.

Figura 4. Nodo de proyectos usados en el proyecto actual

Y básicamente estos es lo diferente… y eso que no me he puesto a añadir configuraciones ni recursos desde la ventana de propiedades… pero eso lo dejo estar… al menos por ahora 😉

 

Todo esto lo estoy escribiendo el 5 de septiembre de 2020 y estoy usando Visual Studio Community 2019 Preview Versión 16.8.0 Preview 2.1 con el .NET 5.0 versión 5.0.100-preview.8.20417.9.

Para estas pruebas he copiado el proyecto para WPF Compilar y ejecutar versión 1.0.0.21 del 31 de agosto de 2020 con la librería gsColorear2008 versión 1.0.6.3 del 8 de enero de 2019.

Nota:
Me apunto actualizar la página de gsColorear en mi sitio para que tenga la última versión tanto de la DLL como de la aplicación.

En la librería de colorear el código, también tuve que quitar los ficheros de recursos (con las palabras clave de los lenguajes) y guardarlos (y abrirlos al usarlos) de forma manual en vez de como si fuese un recurso. No fue complicado, pero… es otra cosa que rompe la compatibilidad entre proyectos. Y esto es independiente de la versión de .NET Core que tenga asignada en el proyecto, ya que lo probé inicialmente con el .NET 5.0 y después con el .NET Core 3.1.
Al final lo he dejado con el .NET Core 3.1, ya que al ser una biblioteca de clases, .NET Core 3.1 si da soporte a ese tipo de proyectos de Visual Basic.

 

Y esto es todo por hoy… otro día más…

Espero que te haya sido de utilidad.

 

Actualización del 16/Sep/2020

He actualizado el código, tanto de la utilidad de compilar y ejecutar como de la DLL de compilar, esta última ahora usa código de Visual Basic en lugar de C#, y también he actualizado la DLL de colorear el código.

Todos esos cambios están en el repositorio de gitHub de gsCompilarEjecutarNET.

Además, he convertido el código de la utilidad a C# y también está publicado en gitHub.

 

Aquí te dejo el enlace original al código que puse cuando publiqué este artículo originalmente el 5 de septiembre.

El enlace para descargar los proyectos Compilar y ejecutar y gsColorearCore para .NET Core

ZIP: Compilar_ejecutar_NetCore_20200905_1710.zip (70.8 KB)
MD5 checksum: ACDC9EF7E2C0F4469719F06D88F8F812

 

Nos vemos.
Guillermo

Tips para crear proyectos de .NET Core (.NET 5.0) en Visual Studio 2019 Preview

Pues eso… leyendo el otro día el artículo Visual Basic support planned for .NET 5.0 (soporte planeado en .NET 5.0 para Visual Basic) indicaba una serie de tipos de proyectos, entre ellos de Windows Forms y WPF, pero no solo para Windows y usando el .NET Framework (que es lo que dicen por ahí que será lo que nos quede en un futuro a los que preferimos usar Visual Basic en lugar de C# (u otro lenguaje de .NET).

Así que… abrí el Visual Studio 2019 (Community) Preview (v16.8.0 Preview 2) y me puse a mirar los tipos de proyectos que había… para C# había de esos dos tipos de aplicaciones tanto para .NET Core como para .NET Framework, pero para Visual Basic solo era para este último marco de trabajo.

Mirando la configuración de los ficheros del proyecto de C# pude crear un proyecto de Windows Forms para Visual Basic que usa el .NET 5.0 basándome en uno de consola (de esos si que hay para el .NET Core o el .NET 5.0).

¡Y funciona!

El problema, es que al agregar los controles al formulario y crear los métodos de evento (haciendo doble-pulsación en el control), los métodos de evento se creaban, pero no estaban conectados con los controles. Y eso es porque al crear los controles (añadiéndolos al formulario) no se definían con WithEvents.

La solución es fácil, se abre el fichero Form1.Designer.vb, se modifica la declaración de los controles (que suelen estar al final de ese fichero) y asunto arreglado… bueno, si le añades el típico Handles después de la declaración del método, por ejemplo:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Label1.Text = "¡Hola Mundo!"
End Sub

Esto último es lo menos engorroso de hacer… lo complicado (o tedioso) es convertir ese proyecto de consola a uno de Windows Forms.
¡Y no te voy a explicar cómo hacerlo! 🙂
No, ya que no es necesario hacer nada si sigues el consejo que te daré a continuación.

Crear proyectos de Visual Basic para .NET Core (o .NET 5.0)

Si no quieres complicarte mucho la vida, haz lo siguiente (tal como se muestra en la figura 1):

Selecciona el menú de Herramientas>Opciones y se muestra la ventana de configuración, selecciona Entorno>Características en versión preliminar y ahí marca la casilla Mostrar todas las plantillas de .NET Core en el cuadro de diálogo nuevo proyecto y pulsa Aceptar. Tendrás que cerrar y abrir nuevamente el Visual Studio y ya tendrás todas las plantillas que hay actualmente disponibles.

 

Usar los nuevos tipos de proyectos

Si ahora creas un nuevo proyecto (o agregas uno a una solución existente) verás que ya están los proyectos de Visual Basic para todas las plataformas (usando Windows Forms y WPF entre otros).

Una vez que seleccionas uno de los proyectos para «todas las plataformas» te dará la opción de elegir la plataforma de destino (el «framework» que usarás). (ver la figura 3)

Si eliges una aplicación de WPF te mostrará las 3 opciones de la figura anterior, si eliges una aplicación de Windows Forms, solo mostrará .NET Core 3.1 y .NET 5.0.

 

NOTA:
Como sabrás .NET 5.0 es una especie de remix entre el .NET Framework y el .NET Core o lo que es lo mismo, es la continuidad de .NET Core, pero unificado con el .NET Framework.
Y la primera versión definitiva está planeada para noviembre de este año de 2020.

 

¿Se soluciona algo al crear así las aplicaciones de Windows Forms?

Pues no… o casi… Me refiero a que al añadir un control al formulario y hacer doble-clic en él se genere correctamente el método de evento.

Ni usando el .NET Core 3.1 ni usando el .NET 5.0 (al menos en Visual Basic) se generan correctamente esos métodos de evento.

Si usas .NET Core 3.1 como plataforma de destino, al menos se definirán los controles con WithEvents, pero tendrás que añadirle el Handles o enlazar el evento y el método con AddHandler.

Si usas como plataforma de destino el .NET 5.0, no se definen los controles con WithEvents y, por tanto, tampoco se crean los métodos con Handles ya que es un requisito el que las variables (controles en este caso) estén definidos con WithEvents para permitir definir los eventos con Handles.

 

A esperar toca…

Esperemos que en futuras revisiones del .NET 5.0 o de Visual Studio esté solucionado, si no… lo tenemos complicado.
No sé quién será el encargado de arreglar esto, supongo que los de VS, y esperemos que sea así, ya que el .NET 5.0 ha entrado en lo que llaman la fase “feature complete” (función completa) en la Preview 8 de hace una semana y ya solo nos quedan las release candidate en las que solo arreglan bugs, no añaden nuevas características.

Habrá que reportarlo como BUG para ver si hacen algo y lo solucionan… porque si no lo solucionan, es que realmente no quieren que los desarrolladores de Visual Basic sigamos usándolo… y… optemos por cambiar a C#… en fin…

 

Nota:
Aunque al crear los proyectos de Windows Forms y WPF aparentemente sean para todas las plataformas, en realidad solo están soportadas en Windows.
O al menos eso quiere decir (o es lo que yo entiendo que significa) esto en la configuración de la aplicación:

<TargetFramework>net5.0-windows</TargetFramework>

 

Espero que te haya servido para algo todo lo aquí comentado… ya sabes que esa es la idea 😉

Nos vemos.
Guillermo