2011-02-04 24 views
41

¿Alguien me puede indicar una buena implementación de un TextBox de Windows Forms básico que mostrará inicialmente texto de marca de agua que desaparece cuando el cursor entra? Creo que puedo crear el mío con un uso creativo de los eventos Enter y Leave, pero estoy seguro de que hay una implementación perfectamente utilizable en algún lugar. Vi la implementación de WPF y, si es necesario, pude anidarla, pero un derivado de WinForms TextBox nativo sería mejor.Watermark TextBox en WinForms

Tengo esto hasta ahora; aún no lo he probado, pero ¿alguien ve problemas evidentes?

public class WatermarkTextBox:TextBox 
{ 
    public string WatermarkText { get; set; } 

    public Color WatermarkColor { get; set; } 

    private Color TextColor { get; set; } 

    private bool isInTransition; 

    public WatermarkTextBox() 
    { 
     WatermarkColor = SystemColors.GrayText; 
    } 

    private bool HasText { get { return Text.IsNotNullOrBlankOr(WatermarkText); }} 

    protected override void OnEnter(EventArgs e) 
    { 
     base.OnEnter(e); 

     if (HasText) return; 

     isInTransition = true; 
     ForeColor = TextColor; 
     Text = String.Empty; 
     isInTransition = false; 
    } 

    protected override void OnForeColorChanged(EventArgs e) 
    { 
     base.OnForeColorChanged(e); 
     if (!isInTransition) //the change came from outside 
      TextColor = ForeColor; 
    } 

    protected override void OnLeave(EventArgs e) 
    { 
     base.OnLeave(e); 

     if (HasText) return; 

     isInTransition = true; 
     ForeColor = WatermarkColor; 
     Text = WatermarkText.EmptyIfNull(); 
     isInTransition = false; 
    } 
} 

EDIT: Lo anterior habría finalmente trabajado con algunos finessing, pero el CueProvider trabajado mucho mejor. Aquí está mi implementación final:

public class WatermarkTextBox:TextBox 
{ 
    private string watermarkText; 
    public string WatermarkText 
    { 
     get { return watermarkText; } 
     set 
     { 
      watermarkText = value; 
      if (watermarkText.IsNullOrBlank()) 
       CueProvider.ClearCue(this); 
      else 
       CueProvider.SetCue(this, watermarkText); 
     } 
    } 
} 

Podría haber integrado la funcionalidad CueProvider por completo, pero esto funciona muy bien.

+6

Una marca de agua es una imagen o patrón en el papel que las etiquetas de forma única de que el papel (llamado una marca de agua digital de fuera del papel). Una bandera de señal es el término para lo que está describiendo. – Tergiver

+0

De la respuesta eliminada de Jay Riggs: Pruebe [CueProvider] (http://www.codeproject.com/kb/edit/cueprovider.aspx) –

Respuesta

76

El término oficial es "señal de aviso". Aquí hay otra manera de hacerlo, simplemente heredando TextBox hace el trabajo también. Agregue una nueva clase a su proyecto y pegue el código que se muestra a continuación. Compilar. Suelte el nuevo control desde la parte superior de la caja de herramientas y configure la propiedad Cue.

Obtiene una vista previa en vivo del valor Cue en el diseñador, localizado en el idioma del formulario. Mucho dinero por muy poco dinero, una excelente demostración de las buenas partes de Winforms.

using System; 
using System.ComponentModel; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

class CueTextBox : TextBox { 
    [Localizable(true)] 
    public string Cue { 
     get { return mCue; } 
     set { mCue = value; updateCue(); } 
    } 

    private void updateCue() { 
     if (this.IsHandleCreated && mCue != null) { 
      SendMessage(this.Handle, 0x1501, (IntPtr)1, mCue); 
     } 
    } 
    protected override void OnHandleCreated(EventArgs e) { 
     base.OnHandleCreated(e); 
     updateCue(); 
    } 
    private string mCue; 

    // PInvoke 
    [DllImport("user32.dll", CharSet = CharSet.Unicode)] 
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, string lp); 
} 
+2

¡Agradable! ¡Funciona de maravilla! – friederbluemle

+6

El problema con este enfoque es que el texto de referencia desaparece una vez que el cuadro de texto obtiene el foco, incluso si no hay texto en el cuadro. Si tiene un formulario con un cuadro de texto que obtiene el foco predeterminado, el cue no será visible para el usuario, lo que invalidará el propósito de la cue. –

+3

Un problema que encontré fue que no es compatible con los controles de Multiline TextBox. No estoy seguro de cómo podría alterar esta clase para admitir Multiline. –

27

He actualizado la respuesta dada por @Hans Passant anterior para introducir constantes, que sea coherente con las definiciones pinvoke.net y dejar que pase la validación FxCop código.

class CueTextBox : TextBox 
{ 
    private static class NativeMethods 
    { 
     private const uint ECM_FIRST = 0x1500; 
     internal const uint EM_SETCUEBANNER = ECM_FIRST + 1; 

     [DllImport("user32.dll", CharSet = CharSet.Unicode)] 
     public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, string lParam); 
    } 

    private string _cue; 

    public string Cue 
    { 
     get 
     { 
      return _cue; 
     } 
     set 
     { 
      _cue = value; 
      UpdateCue(); 
     } 
    } 

    private void UpdateCue() 
    { 
     if (IsHandleCreated && _cue != null) 
     { 
      NativeMethods.SendMessage(Handle, NativeMethods.EM_SETCUEBANNER, (IntPtr)1, _cue); 
     } 
    } 

    protected override void OnHandleCreated(EventArgs e) 
    { 
     base.OnHandleCreated(e); 
     UpdateCue(); 
    } 
} 

Editar: actualización de la llamada PInvoke para establecer CharSet atributo, errar en el lado seguro. Para obtener más información, consulte la página SendMessage en pinvoke.net.

+0

Te agradezco que me cubras la espalda, no soy muy fanático de FxCop (lo cual me pone los pelos de punta). Pero acabo de notar que declaras la versión Unicode W de SendMessage sin usar también CharSet. ¿Estás seguro de que esto funciona? –

+0

@HansPassant Creo que obtuve la definición de [pinvoke.net] (http://www.pinvoke.net/default.aspx/user32/SendMessage.html). Establece que se deben usar los atributos 'MarshalAs' o' CharSet'. Probé ambos, y funcionan bien en XP64 al menos (!). Sin embargo, creo que el uso de 'CharSet' parece más claro: se editará. Hice la 'versión FxCop' para aquellos que tienen que usar FxCop/StyleCop/R # en el trabajo y quiero evitar un par de subrayados rojos :). –

12
[DllImport("user32.dll", CharSet = CharSet.Unicode)] 
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam); 

Y las constantes mensajes:

private const uint EM_SETCUEBANNER = 0x1501; 
private const uint CB_SETCUEBANNER = 0x1703; // minimum supported client Windows Vista, minimum supported server Windows Server 2008 

Y en mi humilde opinión la mejor manera de ponerla en práctica es como un método de extensión.
Así que para el control de cuadro de texto la sintaxis sería:

MyTextBox.CueBanner(false, "Password"); 

A partir del código:

public static void CueBanner(this TextBox textbox, bool showcuewhenfocus, string cuetext) 
{ 
    uint BOOL = 0; 
    if (showcuewhenfocus == true) { BOOL = 1; } 

    SendMessage(textbox.Handle, EM_SETCUEBANNER, (IntPtr)BOOL, cuetext); ; 
} 
+1

No se puede jugar con la declaración de pinvo así. No es correcto para código de 32 bits (devuelve int32) ni código de 64 bits (wparam es int64). Tiende a funcionar por accidente, pero cuando dejas de ser afortunado, entonces no tienes oportunidad de descubrir por qué dejó de funcionar. –

+0

oops, error de escritura, gracias. – deegee

+3

+1 para el método de extensión, exactamente lo que estaba pensando sería lo mejor – tallseth

0
Private Sub randomSubName() Handles txtWatermark.Click 
    txtWatermark.text = "" 
End Sub 

que el texto predeterminado del cuadro de texto lo desea que la marca sea, supongo en este ejemplo, usted nombra el cuadro de texto txtWatermark

Hola, soy nuevo. Así que lo siento si me terriblemente la pata del post ... también tengo ni idea de si esto funciona ...

+3

Bienvenido a StackOverflow. Revise [la guía para responder preguntas] (https://stackoverflow.com/help/how-to-answer). En particular, asegúrese de que algo funcione si va a publicar el código para alguien. – Celeo

7

Aquí es una implementación de un TextBox que apoya mostrando pista (o marca de agua o cue):

  • También muestra la sugerencia cuando MultiLine es verdadero.
  • Se basa en el manejo del mensaje WM_PAINT y el dibujo de la pista. De modo que puede personalizar la sugerencia y agregar algunas propiedades como el color de sugerencia, o puede dibujarlo de derecha a izquierda o controlar cuándo mostrar la sugerencia.
using System.Drawing; 
using System.Windows.Forms; 
public class ExTextBox : TextBox 
{ 
    string hint; 
    public string Hint 
    { 
     get { return hint; } 
     set { hint = value; this.Invalidate(); } 
    } 
    protected override void WndProc(ref Message m) 
    { 
     base.WndProc(ref m); 
     if (m.Msg == 0xf) 
     { 
      if (!this.Focused && string.IsNullOrEmpty(this.Text) 
       && !string.IsNullOrEmpty(this.Hint)) 
      { 
       using (var g = this.CreateGraphics()) 
       { 
        TextRenderer.DrawText(g, this.Hint, this.Font, 
         this.ClientRectangle, SystemColors.GrayText , this.BackColor, 
         TextFormatFlags.Top | TextFormatFlags.Left); 
       } 
      } 
     } 
    } 
} 

se puede descargar un ejemplo de trabajo de la siguiente repositorio:

Si utiliza EM_SETCUEBANNER, entonces habrá 2 números. El texto siempre se mostrará en un color predeterminado del sistema. Además, el texto no se mostrará cuando el TextBox sea MultiLine.

Utilizando la solución de pintura, puede mostrar el texto con el color que desee. También puede mostrar la marca de agua cuando el control es de múltiples líneas:

enter image description here

+0

¿Es posible utilizar esto como una extensión para TextBox Control? – 707

+1

@Yawz Se hereda de 'TextBox'. Después de agregar esta clase a su proyecto, si crea el proyecto, el control 'ExTextBox' se agregará a la caja de herramientas y podrá soltarlo en los formularios. –