Categoría: cosas técnicas

Posts relacionados con la programación o manejo o configuración de los oredenadores (computadores)

  • Scroll sincronizado en varios TextBox en WPF

    Pues eso… que en el programa que publiqué ayer en mi sitio: Compilar y ejecutar (desde una aplicación) utilizo dos textbox con desplazamiento vertical sincronizado, es decir, cuando me desplazo por uno de los TextBox (sea pulsando en la barra de scroll o con la rueda del ratón o con pulsaciones de teclas) el otro se desplaza también.

    Los preliminares

    Aquí te voy a mostrar dos ejemplos, el primero como el de la aplicación esa que te he comentado antes (que solo usa dos controles TextBox, uno de ellos sin mostrar las barras de desplazamiento) (ver figura 1) y el otro en el que hay dos TextBox que se sincronizan tanto vertical como horizontalmente y un tercero que solo se sincroniza verticalmente (ver figura 2).

    Figura 1. Dos textbox sincronizados con el scroll vertical
    Figura 1. Dos textbox sincronizados con el scroll vertical
    Figura 3. Tres textbox sincronizados, dos horizontalmente y los tres verticalmente

    ¿Qué hacer para sincronizar los TextBox?

    Lo que necesitamos es detectar que se hace scroll (desplazamiento) en los controles a sincronizar. Yo he usado el objeto ScrollViewer que contiene un TextBox y por tanto se encarga de sincronizar el contenido del TextBox con las barras de desplazamiento.

    Debemos añadir un evento que detecte que se ha desplazado la barra de scroll (por no repetir desplazamiento) y esto lo podemos hacer en el diseñador XAML indicando que queremos crear el evento que corresponda, en nuestro caso ScrollChanged.

    Para hacerlo, escribimos el nombre del evento y el diseñador de WPF de Visual Studio nos muestra la opción de agregar uno nuevo o usar uno que ya exista (cuando muestra métodos de evento existentes será porque la firma es la misma, es decir, tiene los mismos tipos de argumentos). En la figura 3 puedes ver lo que te estoy comentado.

    Figura 3. Indicamos el evento que queremos usar en un control.
    Figura 3. Indicamos el evento que queremos usar en un control.

    El evento de sincronización, perdón de desplazamiento, cambiado (ScrollChanged) lo tenemos que poner en todos los ScrollViewer que tengamos.

    Y para nuestro ejemplo, solo necesitamos un método de evento, en él haremos las comprobaciones correspondientes para saber qué ScrollViewer ha lanzado el evento.

    Nota:
    Para poder detectar que ha cambiado el desplazamiento (con scroll en las barras) debemos poner el control que queramos (TextBox, ListBox, RichTextBox, etc.) dentro de un ScrollViewer. Ya que esos controles no implementan el evento ScrollChanged.

    Si no quieres detectar ese evento, entonces no hace falta que lo pongas dentro de un ScrollViewer.

    El código de VB y C# para sincronizar los TextBox

    Aquí te muestro el código del evento ScrollChanged que hemos definido en los dos ScrollViewer que tenemos.
    Este es el del ejemplo 1 (Figura 1) en el que solo tenemos dos cajas de texto, una para mostrar los números (así ves mejor que las líneas del otro TextBox están sincronizadas).

    ''' <summary>
    ''' Sincronizar el scroll vertical del código y las filas
    ''' </summary>
    Private Sub SvFilas_ScrollChanged(sender As Object,
                                      e As ScrollChangedEventArgs)
        If inicializando Then Return
    
        inicializando = True
    
        Dim sv = TryCast(sender, ScrollViewer)
        If sv Is svFilas Then
            svCodigo.ScrollToVerticalOffset(e.VerticalOffset)
        Else
            svFilas.ScrollToVerticalOffset(e.VerticalOffset)
        End If
    
        inicializando = False
    End Sub
    
    /// <summary>
    /// Sincronizar el scroll vertical del código y las filas
    /// </summary>
    private void SvFilas_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (inicializando)
            return;
    
        inicializando = true;
    
        var sv = sender as ScrollViewer;
        if (sv == svFilas)
            svCodigo.ScrollToVerticalOffset(e.VerticalOffset);
        else
            svFilas.ScrollToVerticalOffset(e.VerticalOffset);
    
        inicializando = false;
    }
    

    Fíjate que tengo una variable llamada inicializando que he declarado a nivel de clase, esta nos servirá para que no se repita en cascada el evento. Yo normalmente la pongo a False cuando el formulario ha terminado de cargarse.

    Como puedes comprobar lo que hacemos es cambiar o asignar el valor de la propiedad ScrollToVerticalOffset del otro ScrollViewer.

    ¡Así es como se sincronizan! Smile

    Ajustar el contenido del TextBox con los números de líneas

    En el ejemplo que estoy usando una de las cajas de texto muestra el número de línea en el que está actualmente. Esas líneas las tendremos que modificar en consonancia con las líneas que tenga el otro TextBox.

    Así que, tendremos que detectar cuándo se cambia el contenido de la caja de textos normal y agregar las líneas a la otra.

    Lo primero que tenemos que hacer es añadir un método para detectar que el contenido de la caja de textos txtCodigo ha cambiado y asignar a la otra los números para las líneas.

    El evento en cuestión es TextChanged y el código a usar puede ser el que te muestro a continuación.

    El código de VB y C# para procesar el evento TextChanged

    En el siguiente código contaremos las líneas del TextBox utilizando la propiedad LineCount, la cual indica cuántas líneas tiene el control, al menos si es multilínea, es decir, que tenga más de una línea, eso se consigue con la propiedad TextWrapping con un valor Wrap o WrapWithOverflow, el valor predeterminado es NoWrap, que no vale para este ejemplo.

    Nota:
    Los TextBox multilínea de WindowsForms tienen una propiedad Lines que devuelve un array de tipo String con el texto del control. cosa que no ocurre con los TextBox de WPF que solo puedes saber el número de líneas que tiene.

    ''' <summary>
    ''' Al cambiar el texto del código actualizar el número de líneas
    ''' </summary>
    Private Sub TxtCodigo_TextChanged(sender As Object,
                                      e As TextChangedEventArgs)
        ' Contar las líneas
        Dim lin = txtCodigo.LineCount
        txtFilas.Text = ""
        For i = 1 To lin
            ' Indentar el texto a la derecha
            txtFilas.Text &= i.ToString("0").PadLeft(4) & vbCr
        Next
    End Sub
    
    /// <summary>
    /// Al cambiar el texto del código actualizar el número de líneas
    /// </summary>
    private void TxtCodigo_TextChanged(object sender, TextChangedEventArgs e)
    {
        // Contar las líneas
        var lin = txtCodigo.LineCount;
        txtFilas.Text = "";
        for (var i = 1; i <= lin; i++)
            // Indentar el texto a la derecha
            txtFilas.Text += i.ToString("0").PadLeft(4) + "\r";
    }
    

    El PadLeft es para añadir espacios a la izquierda de forma que tenga la cantidad que indicamos, de esta forma los números se alinean bien. No te lo puedo demostrar con las imágenes que he puesto, ya que ninguna empieza por líneas con menos de 2 cifras Smile with tongue out

    El código XAML completo

    Aquí tienes todo el código Xaml para que veas cómo están configurados los controles y de paso te sirva para tener una idea (si no la tienes ya) de cómo ajustar los controles al Grid.

    <Window x:Class="MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Wpf_scroll_dos_textbox_vb"
            mc:Ignorable="d"
            Title="WPF Scroll sincronizado con dos TextBox (VB)" 
            Height="450" Width="800"
            WindowStartupLocation="CenterScreen"
            ResizeMode="CanResizeWithGrip" 
            WindowStyle="ThreeDBorderWindow"
            Loaded="Window_Loaded">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" MinWidth="36"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" MinHeight="24"/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition Height="Auto" MinHeight="24" MaxHeight="40"/>
            </Grid.RowDefinitions>
            <Label x:Name="lblInfo" 
                   Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
                   Background="{DynamicResource {x:Static SystemColors.InfoBrushKey}}"
                   Margin="4,0" HorizontalContentAlignment="Center"
                   Content="Dos TextBox sincronizados verticalmente"/>
            
            <!-- Si no queremos que se vean las barras de desplazamiento
                 las ocultamos en el ScrollViewer 
                 (aunque estén ocultas siguen funcionando) -->
            <ScrollViewer x:Name="svFilas" 
                          Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" 
                          Focusable="False"
                          HorizontalScrollBarVisibility="Hidden"
                          VerticalScrollBarVisibility="Hidden"
                          ScrollChanged="SvFilas_ScrollChanged">
                
                <!-- Si el TextBox está contenido en un ScrollViewer
                    no hace falta indicar en qué columna o fila del Grid está. -->
                <TextBox x:Name="txtFilas" 
                         TextWrapping="Wrap" 
                         FontFamily="Consolas" FontSize="12" 
                         Foreground="DarkCyan"
                         AllowDrop="False" Focusable="False" IsTabStop="False"/>
            </ScrollViewer>
            <ScrollViewer x:Name="svCodigo" 
                          Grid.Column="1" Grid.Row="1" 
                          Grid.ColumnSpan="2" Grid.RowSpan="2"
                          ScrollChanged="SvFilas_ScrollChanged">
                <TextBox x:Name="txtCodigo" 
                         Margin="0,0,2,0" Padding="4,0"
                         TextWrapping="Wrap" 
                         FontFamily="Consolas" FontSize="12" 
                         Text="Aquí irá el texto a mostrar"
                         AcceptsReturn="True" 
                         AcceptsTab="True" 
                         TextChanged="TxtCodigo_TextChanged"/>
            </ScrollViewer>
            <Label x:Name="lblInfo2" 
                   Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3"
                   Background="{DynamicResource {x:Static SystemColors.InfoBrushKey}}"
                   Margin="4,0" HorizontalContentAlignment="Center"
                   Content="Mueve el scroll vertical o la rueda del ratón en el texto, el de la izquierda no es visible, pero puedes desplazarte por el contenido."/>
        </Grid>
    </Window>
    

    El código de VB y C# ya está casi completo y como lo que yo he añadido es un texto de ejemplo que ocupa mucho, pues… me lo ahorro. Ese código lol asigno en el evento Load (Window_Loaded).

    Y esto es todo. Espero que lo sincronices bien Winking smile

    Que no, que no me he olvidado del otro ejemplo con tres controles y la sincronización tanto vertical como horizontal. Pero lo dejo para otro post, que este ya está bien cargadito Smile

    En el otro post (pondré aquí el enlace) te pondré un ZIP con el código completo tanto para Visual Basic como C# en una solución para Visual Studio 2017 con .NET 4.7.2.

    Ahora voy a comer algo y después sigo con el otro post o entrada del blog.
    ¡Buen provecho!
    ¡Gracias!

    Nos vemos.
    Guillermo

  • Compilar y ejecutar

    Compilar y ejecutar es una utilidad creada con WPF (.NET 4.7.2) para compilar y ejecutar aplicaciones tipo consola desde la misma aplicación. Permite guardar el código de VB y C# así como la salida de la consola. Permite colorear el código (tanto de VB como el de C#) y tiene algunos trucos para redirigir la salida de un ejecutable, sincronizar el contenido de dos TextBox, añadir y recuperar texto (o el contenido) de un RichTextBox, y más…

    Para compilar utiliza Roslyn 2.0.1 (Microsoft.CodeDom.Providers.DotNetCompilerPlatform) de esta forma permite el uso de código para compilar con C# 7.3 y VB 15.5 ya que el compilador predeterminado de CodeDom es para las versiones 5 de C@ y 11 de Visual Basic. Por tanto, puedes usar Tuplas si quieres Winking smile

    En la figura 1 puedes ver la aplicación en ejecución con un código de Visual Basic coloreado, mostrando la salida de la aplicación (de consola).

    Figura 1. La aplicación en funcionamiento
    Figura 1. La aplicación en funcionamiento

    En la salida por la consola las vocales acentuadas (con tildes) no se mostraban bien y he usado un truco para convertir el texto de la consola de forma que se pueda leer sin problemas, concretamente usando la codificación de la página de códigos 437 usando GetEncoding(437).

    Este es el código (para VB) que hace la conversión de lo leído por la salida estándar de un objeto Process y convirtiéndolo en texto (un truco para convertir un MemoryStream en texto o cadena).

    ' Convertir la salida usando el código de página 437
    ' que es la usada en MS-DOS (línea de comandos)
    Dim texto = p.StandardOutput.ReadToEnd()
    Dim ms As New MemoryStream(ASCIIEncoding.[Default].GetBytes(texto))
    texto = Encoding.GetEncoding(437).GetString(ms.ToArray())
    

    En otra ocasión te explicaré con más detalle todas las cosillas que la aplicación hace, por ahora te dejo el zip con el código fuente de Visual Basic en una solución para Visual Studio 2017 además del ejecutable y si prefieres instalarla usando ClickOnce, también lo puedes hacer, más abajo tienes los enlaces.

    Comentarte que en el ZIP se incluye los packages de Roslyn.

    Y esto ews todo por ahora… Smile

    Espero que te sea de utilidad

    Nos vemos.
    Guillermo

    P.S (27-feb-2021 15:19)
    No sé porqué esto lo tenía como borrador… seguramente porque no estará completa la entrada, pero… así la dejo 😉

  • ¿Quieres aprender a programar usando .NET Core sin instalar nada?

    Pues eso… me he metido en la página de instalación del .NET Core 3.0 SDK y he visto un botón con el enlace «Get Started» (ver la figura 1) y me ha llevado a la página de .NET Tutorial – Hello World in 10 minutes y en la parte de la izquierda he visto un botón con el texto Try .NET in your browser (ver figura 2) y lo he pulsado… y me ha llevado a una página en la que te va explicando las cosas básicas para crear una aplicación de consola, empezando con el clásico «Hello World!» (ver figura 3) que después ha pasado al «Hello Guillermo!» (o tu nombre) y ha seguido haciendo cambios y enseñando algunas cosas, que al menos yo ya había leído, pero así lo he tenido más claro (ver figura 4)…. bueno, es curioso más que nada… y no acaba ahí la cosa (yo no he seguido) pero se ve que sigue con otras cosas como trabajar con números, aunque esta vez ha cambiado el formato, pero básicamente es en el mismo tono (ver figuras 5 y 6), así que… te lo recomiendo ya que las cosas que incluyen son:

    Análisis de las operaciones matemáticas con enteros
    Análisis sobre el orden de las operaciones
    Información sobre los límites y la precisión de los enteros
    Operaciones con el tipo double
    Operaciones con tipos de punto fijo

    Eso sí, todo con puntos y comas… Smile

    Las capturas

    Figura 1. Get Started en la página de descarga del .NET Core 3.0 SDK
    Figura 1. Get Started en la página de descarga del .NET Core 3.0 SDK

    Figura 1.

    Figura 2. El tutorial con el botón de Try .NET in your browser
    Figura 2. El tutorial con el botón de Try .NET in your browser

    Figura 2.

    Figura 3. El tutorial de .NET In-Browser
    Figura 3. El tutorial de .NET In-Browser

    Figura 3.

    Figura 4. String Interpolation
    Figura 4. String Interpolation

    Figura 4.

    Figura 5. Aquí acaba el tutorial .NET In-Browser
    Figura 5. Aquí acaba el tutorial .NET In-Browser

    Figura 5.

    Figura 6. El tutorial de C# sigue con el compilador In-Browser
    Figura 6. El tutorial de C# sigue con el compilador In-Browser

    Los enlaces:

    Espero que si no te es de utilidad al menos te relaje un poco… jajajaja

    Nos vemos.
    Guillermo