¿Cómo puedo agregar un texto de sugerencia al cuadro de texto de WPF?

107

Por ejemplo, Facebook tiene un texto de sugerencia de "Buscar" en el cuadro de texto Buscar cuando el cuadro de texto está vacío.

¿Cómo lograr esto con los cuadros de texto de WPF?

Cuadro de texto de búsqueda de Facebook

Louis Rhys
fuente
2
Intente buscar "banner de señal".
configuración regional predeterminada
@MAKKAM este artículo de MSDN lo analiza pero no muestra cómo se hace
Louis Rhys
1
No llamaría lo que estás pidiendo "texto de sugerencia". para mí, el texto de sugerencia es una ventana emergente. Sin embargo, encontré esta pregunta cuando quería configurar el texto del marcador de posición. y las respuestas a continuación me ayudaron.
steve
1
Eso se llama marca de agua por cierto
Blechdose

Respuestas:

158

Puede lograr esto mucho más fácilmente con VisualBrushay algunos desencadenantes en a Style:

<TextBox>
    <TextBox.Style>
        <Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
            <Style.Resources>
                <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                    <VisualBrush.Visual>
                        <Label Content="Search" Foreground="LightGray" />
                    </VisualBrush.Visual>
                </VisualBrush>
            </Style.Resources>
            <Style.Triggers>
                <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="Text" Value="{x:Null}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="IsKeyboardFocused" Value="True">
                    <Setter Property="Background" Value="White" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

Para aumentar la reutilización de esto Style, también puede crear un conjunto de propiedades adjuntas para controlar el texto, el color, la orientación, etc.

sellmeadog
fuente
1
Utilice IsMouseCaptured en lugar de IsKeyboardFocused. Así es como responde un banner de señal real.
Monstieur
5
Si alguien se pregunta cómo utilizar las propiedades adjuntas para aumentar la reutilización del estilo, consulte: stackoverflow.com/a/650620/724944
surfen
@Kurian IsMouseCaptured hará que la señal desaparezca solo cuando haga clic con el mouse, pero volverá a aparecer cuando suelte el botón del mouse. No luce bien. IsMouseOver tampoco sería bueno (el teclado tiene el foco pero el puntero del mouse está en otra parte => se muestra la señal). La mayoría de los banners de señal usan IsKeyboardFocused (Facebook, por ejemplo) y creo que está bien. La solución alternativa sería usar ambos disparadores: (IsMouseOver OR IsKeyboardFocused)
surfen
23
La solución debería ser Hint = "Por favor ingrese su texto" no 20 elementos ... Por desgracia, esto no es compatible con el legendario cuadro de texto integrado ...
Mzn
8
Si bien este enfoque puede estar bien para las condiciones predeterminadas, no funciona cuando su cuadro de texto ya contiene un pincel de fondo o el fondo del formulario no es del mismo color que el cuadro de texto.
LWChris
56

Esta es mi solución simple, adaptada de Microsoft ( https://code.msdn.microsoft.com/windowsapps/How-to-add-a-hint-text-to-ed66a3c6 )

    <Grid Background="White" HorizontalAlignment="Right" VerticalAlignment="Top"  >
        <!-- overlay with hint text -->
        <TextBlock Margin="5,2" MinWidth="50" Text="Suche..." 
                   Foreground="LightSteelBlue" Visibility="{Binding ElementName=txtSearchBox, Path=Text.IsEmpty, Converter={StaticResource MyBoolToVisibilityConverter}}" />
        <!-- enter term here -->
        <TextBox MinWidth="50" Name="txtSearchBox" Background="Transparent" />
    </Grid>
Martin Schmidt
fuente
Enfoque interesante No habría pensado en mí de inmediato.
itsmatt
agradable y simple :)
shmulik.r
3
Esta solución funciona especialmente bien si configura TextBlock enIsHitTestVisible="False"
Mage Xy
1
@MageXy se refiere al primer TextBlock (el de sugerencia).
Felix
Esta solución funciona muy bien si desea vincular el texto de la sugerencia a una propiedad.
PeterB
11

¿Qué pasa con el uso de materialDesign HintAssist? Estoy usando esto, que también puede agregar una pista flotante:

<TextBox Width="150" Height="40" Text="hello" materialDesign:HintAssist.Hint="address"  materialDesign:HintAssist.IsFloating="True"></TextBox>

instalé Material Design con Nuget Package, hay una guía de instalación en el enlace de documentación

Mahdi Khalili
fuente
2
Biblioteca muy útil
AlexF11
9

Hágalo en el código subyacente estableciendo el color del texto inicialmente en gris y agregando controladores de eventos para ganar y perder el enfoque del teclado.

TextBox tb = new TextBox();
tb.Foreground = Brushes.Gray;
tb.Text = "Text";
tb.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(tb_GotKeyboardFocus);
tb.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(tb_LostKeyboardFocus);

Luego, los controladores de eventos:

private void tb_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    if(sender is TextBox)
    {
        //If nothing has been entered yet.
        if(((TextBox)sender).Foreground == Brushes.Gray)
        {
            ((TextBox)sender).Text = "";
            ((TextBox)sender).Foreground = Brushes.Black;
        }
    }
}


private void tb_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    //Make sure sender is the correct Control.
    if(sender is TextBox)
    {
        //If nothing was entered, reset default text.
        if(((TextBox)sender).Text.Trim().Equals(""))
        {
            ((TextBox)sender).Foreground = Brushes.Gray;
            ((TextBox)sender).Text = "Text";
        }
    }
}
mxgg250
fuente
7
-1 por hacerlo en código subyacente: satura el control, y existe una alta probabilidad de que esto interfiera con otra lógica de control, si no ahora, en el futuro.
AlexeiOst
4

Puedes hacerlo de una forma muy sencilla. La idea es colocar una etiqueta en el mismo lugar que su cuadro de texto. Su etiqueta será visible si el cuadro de texto no tiene texto y no tiene el foco.

 <Label Name="PalceHolder"  HorizontalAlignment="Left" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Height="40" VerticalAlignment="Top" Width="239" FontStyle="Italic"  Foreground="BurlyWood">PlaceHolder Text Here
  <Label.Style>
    <Style TargetType="{x:Type Label}">
      <Setter Property="Visibility" Value="Hidden"/>
      <Style.Triggers>
        <MultiDataTrigger>
          <MultiDataTrigger.Conditions>
            <Condition Binding ="{Binding ElementName=PalceHolder, Path=Text.Length}" Value="0"/>
            <Condition Binding ="{Binding ElementName=PalceHolder, Path=IsFocused}" Value="False"/>
          </MultiDataTrigger.Conditions>
          <Setter Property="Visibility" Value="Visible"/>
        </MultiDataTrigger>
      </Style.Triggers>
    </Style>
  </Label.Style>
</Label>
<TextBox  Background="Transparent" Name="TextBox1" HorizontalAlignment="Left" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Height="40"TextWrapping="Wrap" Text="{Binding InputText,Mode=TwoWay}" VerticalAlignment="Top" Width="239" />

Bonificación: si desea tener un valor predeterminado para su cuadro de texto, asegúrese de configurarlo después al enviar datos (por ejemplo: "InputText" = "PlaceHolder Text Here" si está vacío).

maquillar
fuente
2

Otro enfoque ;-)

esto también funciona con PasswordBox. Si desea usarlo con TextBox, simplemente intercambie PasswordChangedcon TextChanged.

XAML:

<Grid>
    <!-- overlay with hint text -->
    <TextBlock Margin="5,2"
                Text="Password"
                Foreground="Gray"
                Name="txtHintPassword"/>
    <!-- enter user here -->
    <PasswordBox Name="txtPassword"
                Background="Transparent"
                PasswordChanged="txtPassword_PasswordChanged"/>
</Grid>

Código detrás:

private void txtPassword_PasswordChanged(object sender, RoutedEventArgs e)
{
    txtHintPassword.Visibility = Visibility.Visible;
    if (txtPassword.Password.Length > 0)
    {
        txtHintPassword.Visibility = Visibility.Hidden;
    }
}
Estera
fuente
¡La mejor y más simple solución que he encontrado! ¡¡Gracias!!
steve
2

Una vez me metí en la misma situación, la resolví de la siguiente manera. Solo he cumplido con los requisitos de un cuadro de sugerencias, puede hacerlo más interactivo agregando efectos y otras cosas en otros eventos como el enfoque, etc.

CÓDIGO WPF (he eliminado el estilo para que sea legible)

<Grid Margin="0,0,0,0"  Background="White">
    <Label Name="adminEmailHint" Foreground="LightGray" Padding="6"  FontSize="14">Admin Email</Label>
    <TextBox Padding="4,7,4,8" Background="Transparent" TextChanged="adminEmail_TextChanged" Height="31" x:Name="adminEmail" Width="180" />
</Grid>
<Grid Margin="10,0,10,0" Background="White" >
    <Label Name="adminPasswordHint" Foreground="LightGray" Padding="6"  FontSize="14">Admin Password</Label>
    <PasswordBox Padding="4,6,4,8" Background="Transparent" PasswordChanged="adminPassword_PasswordChanged" Height="31" x:Name="adminPassword" VerticalContentAlignment="Center" VerticalAlignment="Center" Width="180" FontFamily="Helvetica" FontWeight="Light" FontSize="14" Controls:TextBoxHelper.Watermark="Admin Password"  FontStyle="Normal" />
</Grid>

Código C #

private void adminEmail_TextChanged(object sender, TextChangedEventArgs e)
    {
        if(adminEmail.Text.Length == 0)
        {
            adminEmailHint.Visibility = Visibility.Visible;
        }
        else
        {
            adminEmailHint.Visibility = Visibility.Hidden;
        }
    }

private void adminPassword_PasswordChanged(object sender, RoutedEventArgs e)
    {
        if (adminPassword.Password.Length == 0)
        {
            adminPasswordHint.Visibility = Visibility.Visible;
        }
        else
        {
            adminPasswordHint.Visibility = Visibility.Hidden;
        }
    }
Mohammad Mahroz
fuente
1

Otra solución es utilizar un kit de herramientas de WPF como MahApps.Metro. Tiene muchas características interesantes, como una marca de agua en un cuadro de texto:

Controls:TextBoxHelper.Watermark="Search..."

Ver http://mahapps.com/controls/textbox.html

StefanG
fuente
0

Usé los eventos de enfoque conseguido y perdido:

Private Sub txtSearchBox_GotFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles txtSearchBox.GotFocus
    If txtSearchBox.Text = "Search" Then
        txtSearchBox.Text = ""
    Else

    End If

End Sub

Private Sub txtSearchBox_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles txtSearchBox.LostFocus
    If txtSearchBox.Text = "" Then
        txtSearchBox.Text = "Search"
    Else

    End If
End Sub

Funciona bien, pero el texto todavía está en gris. Necesita limpieza. Estaba usando VB.NET

StarLordBlair
fuente
0
  <Grid>
    <TextBox Name="myTextBox"/>
    <TextBlock>
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ElementName=myTextBox, Path=Text.IsEmpty}" Value="True">
                        <Setter Property="Text" Value="Prompt..."/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</Grid>
DerBesondereEin
fuente
0

Esa es mi opinión:

<ControlTemplate>
    <Grid>
        <Grid.Resources>
            <!--Define look / layout for both TextBoxes here. I applied custom Padding and BorderThickness for my application-->
            <Style TargetType="TextBox">
                <Setter Property="Padding" Value="4"/>
                <Setter Property="BorderThickness" Value="2"/>
            </Style>
        </Grid.Resources>

        <TextBox x:Name="TbSearch"/>
        <TextBox x:Name="TbHint" Text="Suche" Foreground="LightGray"
                 Visibility="Hidden" IsHitTestVisible="False" Focusable="False"/>
    </Grid>

    <ControlTemplate.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition SourceName="TbSearch" Property="Text" Value="{x:Static sys:String.Empty}"/>
                <Condition SourceName="TbSearch" Property="IsKeyboardFocused" Value="False"/>
            </MultiTrigger.Conditions>
            <MultiTrigger.Setters>
                <Setter TargetName="TbHint" Property="Visibility" Value="Visible"/>
            </MultiTrigger.Setters>
        </MultiTrigger>

        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition SourceName="TbSearch" Property="Text" Value="{x:Null}"/>
                <Condition SourceName="TbSearch" Property="IsKeyboardFocused" Value="False"/>
            </MultiTrigger.Conditions>
            <MultiTrigger.Setters>
                <Setter TargetName="TbHint" Property="Visibility" Value="Visible"/>
            </MultiTrigger.Setters>
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

La mayoría de las otras respuestas, incluida la de arriba, tienen defectos en mi opinión.

Esta solución funciona en todas las circunstancias. XAML puro, fácilmente reutilizable.

Julian
fuente
-1

Puedo lograr esto con un VisualBrushy algunos disparadores en un Stylerecomienda: sellmeadog.

<TextBox>
        <TextBox.Style>
            <Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
                <Style.Resources>
                    <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                        <VisualBrush.Visual>
                            <Label Content="Search" Foreground="LightGray" />
                        </VisualBrush.Visual>
                    </VisualBrush>
                </Style.Resources>
                <Style.Triggers>
                    <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                        <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                    </Trigger>
                    <Trigger Property="Text" Value="{x:Null}">
                        <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                    </Trigger>
                    <Trigger Property="IsKeyboardFocused" Value="True">
                        <Setter Property="Background" Value="White" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

@sellmeadog: Aplicación en ejecución, bt Design no cargando ... aparece el siguiente error: Referencia de tipo ambigua. Un tipo llamado 'StaticExtension' ocurre en al menos dos espacios de nombres, 'MS.Internal.Metadata.ExposedTypes.Xaml' y 'System.Windows.Markup'. Considere ajustar los atributos XmlnsDefinition del ensamblado. estoy usando .net 3.5

SUHAIL AG
fuente
Resolvió el problema cambiando <Trigger Property="Text" Value="{x:Static sys:String.Empty}">a<Trigger Property="Text" Value="">
SUHAIL AG
6
Esto parece ser una respuesta a otra publicación. Por favor, solo publique las soluciones completas a la pregunta como respuestas.
Drew Gaynor
4
Si cree que la respuesta de @ sellmeadog es casi correcta, considere corregirla, en lugar de publicar una nueva respuesta casi sin diferencias.
0xBADF00D
-11

Para WPF, no hay forma. Tienes que imitarlo. Vea este ejemplo . Una secundaria (solución inestable) es alojar un control de usuario de WinForms que hereda de TextBox y enviar el mensaje EM_SETCUEBANNER al control de edición. es decir.

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam);

private const Int32 ECM_FIRST = 0x1500;
private const Int32 EM_SETCUEBANNER = ECM_FIRST + 1;

private void SetCueText(IntPtr handle, string cueText) {
    SendMessage(handle, EM_SETCUEBANNER, IntPtr.Zero, Marshal.StringToBSTR(cueText));
}

public string CueText {
    get {
        return m_CueText;
    } 
    set {
        m_CueText = value;
        SetCueText(this.Handle, m_CueText);
}

Además, si desea alojar un enfoque de control de WinForm, tengo un marco que ya incluye esta implementación llamado BitFlex Framework, que puede descargar de forma gratuita aquí .

Aquí hay un artículo sobre BitFlex si desea más información. Comenzará a encontrar que si está buscando tener controles de estilo del Explorador de Windows, esto generalmente nunca sale de la caja, y debido a que WPF no funciona con identificadores, generalmente no puede escribir un contenedor fácil alrededor de Win32 o un control existente como puede con WinForms.

Captura de pantalla: ingrese la descripción de la imagen aquí

David Anderson
fuente
1
wow, eso parece un poco pirateado. ¿Cómo puedo hacer esto al crear un control de usuario con XAML?
Louis Rhys
Tu no Así es como se hace. Si desea encapsular esto, cree un control de usuario y una propiedad CueText, y llame a SetCueText en el setter.
David Anderson
Supongo que OP debería alojar controles winforms para utilizar este enfoque. ¿O hay alguna forma de obtener el identificador del cuadro de texto?
configuración regional predeterminada
Esto parece algo que podría hacerse de forma declarativa con WPF, vinculando si el cuadro de texto tiene foco o no, etc. - El ejemplo anterior es más un enfoque de WinForms - funcionará en WPF, pero no es el correcto camino.
BrainSlugs83
1
Lo siento, pero esta respuesta es simplemente incorrecta. Además, todos los enlaces están rotos.
Forest Kunecke