Moderniza tu aplicación con el efecto grisáceo en el texto de los controles con nota de lo que hay que escribir en VB y C#

Pues eso… seguramente habrás visto en algunas aplicaciones que en las cajas de texto (o los ComboBox) se muestra un texto en color gris para indicarte lo que puedes escribir en esa casilla.

Para tener esa funcionalidad en nuestros proyectos no es necesario usar controles especializados ni nada de eso, ya verás que es fácil, y espero que claro, hacerlo mediante unas pocas líneas de código.

Actualizado y corregido el código (24-oct-2020)
Ver más abajo el comentario y el nuevo código de QuitarPredeterminado.

 

En la figura 1 puedes ver el aspecto de esa caja de texto.

Figura 1. La aplicación en modo ejecución

En la imagen anterior puedes ver el texto <Escribe tu nombre> en la caja de texto, cuando empieces a escribir, ese texto informativo se quita y solo estará lo que escribas, y si borras todo el texto (dejas vacía la caja de texto) se volverá a mostrar ese texto informativo.

¿Dónde controlar si se debe mostrar o no el texto predeterminado?

En mi caso, yo hago las comprobaciones en tres eventos del (en este caso) TextBox. A saber:

El evento Enter (cuando toma el foco) ahí se comprueba si debe tener el color grisáceo o el negro del texto.
Se comprueba si el texto es diferente del predeterminado (el de ayuda o informativo), en ese caso se asigna el color del texto a ControlText (definido en SystemColors).

El evento Leave (cuando pierde el foco) ahí compruebo si no hay texto, en cuyo caso, asigno el texto informativo y le asigno a la propiedad ForeColor el color GrayText (también de la clase SystemColors).

El evento TextChange (cuando el texto cambia), hay lo que hay que hacer es más elaborado (menos simple que en los casos anteriores), al menos como yo lo he hecho, que puede que haya un método más fácil y simple de hacerlo. Pero es lo que le da esa vidilla al efecto ;-).

Yo suelo usar una variable a nivel de formulario llamada inicializando que me sirve para evitar la entrada en cascada del evento TextChange (y otros eventos, pero en este ejemplo es el que se produce cuando el texto cambia). Si el valor de esa variable es True, salimos del método.

La primera comprobación es si el texto del control está vacío o tiene el texto predeterminado, en cuyo caso lo ponemos en color gris y le asigno el texto informativo.
Si no se da esa comprobación, el texto ya tiene algo, por ejemplo, si se ha empezado a escribir, y compruebo si el texto que había antes era el texto vacío en cuyo caso le quito el texto predeterminado con idea de que solo se quede lo que el usuario está escribiendo.

La primera idea que tuve fue quitar directamente el texto predeterminado con un Replace(textoPredeterminado, «»), pero eso solo vale si se está escribiendo al principio o al final del texto (que es lo más común), pero si el usuario se pone a escribir en medio del texto predeterminado, no funcionaría… por tanto, he creado una función en la que quito todo el texto predeterminado del texto que haya, para así dejar solo lo que se esté escribiendo, que será la primera letra que el usuario escriba.

Esa función yo la tengo como una extensión de la clase String, pero en el código que te mostraré, es simplemente una función llamada QuitarPredeterminado que recibe dos argumentos, el texto a comprobar y el texto a quitar. De esa forma nos servirá para aplicarla a cualquier control y evitar tener que repetir lo mismo en todos los evento TextChange de los controles a los que queramos dar esta funcionalidad.

Aquí (y en la comprobación anterior) entra en juego la variable inicializando, que es la que evitará que se entre nuevamente mientras modificamos el texto.

Finalmente asignamos a la variable que contiene el texto anterior, el texto actual para que no se quite nada cuando ya no sea el texto predeterminado, ya que es posible que el usuario escriba en el texto ese mismo texto que damos por predeterminado, el problema es que el usuario solo escriba el texto predeterminado, pero… bueno, se supone que no lo escribirá… 😉

Y esto es todo lo que hay que hacer… viendo ahora el código para Visual Basic y C# lo entenderás mejor.

 

Rectificación del código (24-oct-2020)

Pues resulta que hay que hacer una comprobación extra antes de quitar los caracteres del texto predeterminado, si no, puede pasar lo que me pasó anoche, que al asignar la palabra InitializeComponent a la caja de textos donde está la palabra predeterminada Buscar… pues… se encontró con la a (de Buscar) y la quitó…
Así que… ahora compruebo primero si están todas las letras del texto predeterminado y de ser así, entonces hago el reemplazo, si no están todas las letras, simplemente se devuelve el texto original y como si nada.

El código de QuitarPredeterminado que te muestro más abajo ya tiene las modificaciones indicadas.

El código completo, tanto para VB como para C#, publicado en github lo actualizaré en unos minutos.

El código de ejemplo para Visual Basic .NET y C#

Empezaré mostrándote las variables que necesitaremos a nivel de formulario.
Es decir, la que controla si ya estamos dentro del evento, la que contiene el texto predeterminado y la que contiene el texto anterior de la caja de texto.

Estas dos últimas serán diferentes para cada control a los que queramos aplicar el efecto. Al menos la del texto anterior, ya que el texto predeterminado puede ser el mismo para varios controles.

Visual Basic .NET

Private Const textVacio As String = "<Escribe tu nombre>"
Private textAnterior As String = textVacio
Private inicializando As Boolean

C#

private const string textVacio = "<Escribe tu nombre>";
private string textAnterior = textVacio;
private bool inicializando;

 

Ahora te muestro el código de los eventos Enter y Leave.

Visual Basic .NET

Private Sub txtTexto_Enter(sender As Object, e As EventArgs) Handles txtTexto.Enter
    If txtTexto.Text <> textVacio Then
        txtTexto.ForeColor = SystemColors.ControlText
    End If
End Sub

Private Sub txtTexto_Leave(sender As Object, e As EventArgs) Handles txtTexto.Leave
    If String.IsNullOrEmpty(Me.txtTexto.Text) Then '
        Me.txtTexto.ForeColor = SystemColors.GrayText
        Me.txtTexto.Text = textVacio
    End If
End Sub

 

C#

private void txtTexto_Enter(object sender, EventArgs e)
{
    if (txtTexto.Text != textVacio)
        txtTexto.ForeColor = SystemColors.ControlText;
}

private void txtTexto_Leave(object sender, EventArgs e)
{
    if (string.IsNullOrEmpty(this.txtTexto.Text))
    {
        this.txtTexto.ForeColor = SystemColors.GrayText;
        this.txtTexto.Text = textVacio;
    }
}

 

El código del evento TextChanged en el que uso la función para quitar el texto predeterminado esté en la posición que esté.
Después te muestro el código de esa función QuitarPredeterminado.

Visual Basic .NET

Private Sub txtTexto_TextChanged(sender As Object,
                                 e As EventArgs) Handles txtTexto.TextChanged
    If inicializando Then Return
    If txtTexto.Text = "" OrElse txtTexto.Text = textVacio Then
        txtTexto.ForeColor = SystemColors.GrayText
        inicializando = True
        txtTexto.Text = textVacio
        inicializando = False
    Else
        If textAnterior = textVacio Then
            inicializando = True
            txtTexto.Text = QuitarPredeterminado(txtTexto.Text, textVacio)
            inicializando = False

            txtTexto.SelectionStart = txtTexto.Text.Length
        End If
        txtTexto.ForeColor = SystemColors.ControlText
    End If
    textAnterior = txtTexto.Text
End Sub

 

C#

private void txtTexto_TextChanged(object sender, EventArgs e)
{
    if (inicializando)
        return;
    if (txtTexto.Text == "" || txtTexto.Text == textVacio)
    {
        txtTexto.ForeColor = SystemColors.GrayText;
        inicializando = true;
        txtTexto.Text = textVacio;
        inicializando = false;
    }
    else
    {
        if (textAnterior == textVacio)
        {
            inicializando = true;
            txtTexto.Text = QuitarPredeterminado(txtTexto.Text, textVacio);
            inicializando = false;

            txtTexto.SelectionStart = txtTexto.Text.Length;
        }
        txtTexto.ForeColor = SystemColors.ControlText;
    }
    textAnterior = txtTexto.Text;
}

 

Fíjate que antes de asignar un texto al TextBox asigno el valor true a inicializando, de esa forma, cuando se cambie el texto (al asignarlo se cambiará), no entrará nuevamente en el evento.

Y como te comentaba antes, cuando el texto que había anterior es el predeterminado (textVacio), quitamos dicho texto de la propiedad Text de la caja de textos mediante una llamada al método QuitarPredeterminado, al que le pasamos el texto que queremos comprobar (el del control TextBox) y el que queremos quitar.

Veamos el código de ese método… y verás lo casi retorcido que es… 🙂
En serio, lo que hago es recorrer cada carácter del texto a quitar (el predeterminado) y quitarlo del texto, de esa forma, esté donde esté ese carácter que queremos quitar lo hará correctamente.

Aquí tienes el código de la función QuitarPredeterminado.

Visual Basic .NET

''' <summary>
''' Quitar de una cadena un texto indicado (que será el predeterminado cuando está vacío).
''' Por ejemplo si el texto grisáceo es Buscar... y
''' se empezó a escribir en medio del texto (o en cualquier parte)
''' BuscarL... se quitará Buscar... y se dejará L.
''' Antes de hacer cambios se comprueba si el texto predeterminado está al completo 
''' en el texto en el que se hará el cambio.
''' </summary>
''' <param name="texto">El texto en el que se hará la sustitución.</param>
''' <param name="predeterminado">El texto a quitar.</param>
''' <returns>Una cadena con el texto predeterminado quitado.</returns>
''' <remarks>18/Oct/2020 actualizado 24/Oct/2020</remarks>
Public Function QuitarPredeterminado(texto As String, predeterminado As String) As String
    Dim cuantos = predeterminado.Length
    Dim k = 0

    For i = 0 To predeterminado.Length - 1
        Dim j = texto.IndexOf(predeterminado(i))
        If j = -1 Then Continue For
        k += 1
    Next
    ' si k es distinto de cuantos es que no están todos lo caracteres a quitar
    If k <> cuantos Then
        Return texto
    End If

    For i = 0 To predeterminado.Length - 1
        Dim j = texto.IndexOf(predeterminado(i))
        If j = -1 Then Continue For
        If j = 0 Then
            texto = texto.Substring(j + 1)
        Else
            texto = texto.Substring(0, j) & texto.Substring(j + 1)
        End If
    Next

    Return texto
End Function

C#

/// <summary>
/// Quitar de una cadena un texto indicado (que será el predeterminado cuando está vacío).
/// Por ejemplo si el texto grisáceo es Buscar... y
/// se empezó a escribir en medio del texto (o en cualquier parte)
/// BuscarL... se quitará Buscar... y se dejará L.
/// Antes de hacer cambios se comprueba si el texto predeterminado está al completo 
/// en el texto en el que se hará el cambio.
/// </summary>
/// <param name="texto">El texto en el que se hará la sustitución.</param>
/// <param name="predeterminado">El texto a quitar.</param>
/// <returns>Una cadena con el texto predeterminado quitado.</returns>
/// <remarks>18/Oct/2020 actualizado 24/Oct/2020</remarks>
private string QuitarPredeterminado(string texto, string predeterminado)
{
    var cuantos = predeterminado.Length;
    var k = 0;

    for (var i = 0; i < predeterminado.Length; i++)
    {
        var j = texto.IndexOf(predeterminado[i]);
        if (j == -1)
            continue;
        k += 1;
    }
    // si k es distinto de cuantos es que no están todos lo caracteres a quitar
    if (k != cuantos)
        return texto;

    for (var i = 0; i < predeterminado.Length; i++)
    {
        var j = texto.IndexOf(predeterminado[i]);
        if (j == -1)
            continue;
        if (j == 0)
            texto = texto.Substring(j + 1);
        else
            texto = texto.Substring(0, j) + texto.Substring(j + 1);
    }
    return texto;
}

 

Y esto es todo… yo lo estoy usando de varias formas, por ejemplo en los TextBox (en realidad un ComboBox del tipo ToolStripComboBox) para Buscar y otro para Reemplazar, en el primero muestro de forma predeterminada el texto Buscar… y el de reemplazar el texto Reemplazar….

La idea la tomé prestada, con estas mejoras que te he mostrado aquí, del proyecto CSharp2VB de Paul1956.

 

Publicaré Ya está el código completo de ejemplo tanto para Visual Basic como para C# (proyectos para .NET Framework 4.8, aunque sirven igualmente con proyectos para .NET 5.0 RC2) publicado en GitHub: Mostrar-texto-grisaceo.

 

Espero que te sea de utilidad.

 

Nos vemos.
Guillermo

2 comentarios en “Moderniza tu aplicación con el efecto grisáceo en el texto de los controles con nota de lo que hay que escribir en VB y C#

  1. Alex

    Creo que es una mala idea usar la propiedad Text para poner el texto gris cuando no hay nada ingresado, ya que ese valor es usado para indicar el texto que ingresó el usuario, por lo que usarlo como placeholder hace muy complicado el codigo.
    El Textbox de Windows tiene una propiedad Placeholder para poner el texto gris pero no está expuesta en .NET Framework, hay que usar el API de Windows.
    Otra forma es pintar el texto encima del Textbox luego que el Textbox se haya pintado, hacerlo simplemente usando el evento Paint no es posible, hay que usar un NativeWindow para adquirir el Handle del Textbox y pintar sobre él.
    Este link dice como pintar sobre el Textbox con un NativeWindow:
    https://web.archive.org/web/20071208020623/www.codedblog.com/2007/09/17/owner-drawing-a-windowsforms-textbox/

    Responder

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *