Como posso adicionar um texto de dica à caixa de texto WPF?

107

Por exemplo, o Facebook tem um texto de dica "Pesquisar" na caixa de texto Pesquisar quando a caixa de texto está vazia.

Como fazer isso com caixas de texto WPF ??

Caixa de texto de pesquisa do Facebook

Louis Rhys
fonte
2
Tente pesquisar por "banner cue".
localidade padrão
@MAKKAM este artigo do MSDN discute isso, mas não mostra como é feito
Louis Rhys,
1
Eu não chamaria o que você está pedindo de 'texto de dica'. para mim, o texto da dica é uma janela pop-up. no entanto, encontrei esta questão quando queria configurar o texto do espaço reservado. e as respostas abaixo me ajudaram.
steve
1
A propósito, isso é chamado de marca d'água
Blechdose

Respostas:

158

Você pode fazer isso muito mais facilmente com um VisualBrushe alguns gatilhos em 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 a reutilização disso Style, você também pode criar um conjunto de propriedades anexadas para controlar o texto real do banner de sugestão, cor, orientação etc.

sellmeadog
fonte
1
Use IsMouseCaptured em vez de IsKeyboardFocused. É assim que um banner cue real responde.
Monstieur
5
Se alguém tiver dúvidas sobre como usar as propriedades anexadas para aumentar a capacidade de reutilização do estilo, consulte: stackoverflow.com/a/650620/724944
surfen
@Kurian IsMouseCaptured fará com que o cue desapareça apenas quando você clicar nele com o mouse, mas aparecerá novamente quando você soltar o botão do mouse. Não parece bom. IsMouseOver também não seria bom (o teclado tem foco, mas o ponteiro do mouse está em outro lugar => sugestão exibida). A maioria dos banners de sinalização usa IsKeyboardFocused (Facebook, por exemplo) e acho que está tudo bem. A solução alternativa seria usar os dois gatilhos: (IsMouseOver OR IsKeyboardFocused)
navegar em
23
A solução deve ser Hint = "Digite seu texto" e não 20 elementos ... Infelizmente, isso não é compatível com a caixa de texto lendária embutida ...
Mzn
8
Embora essa abordagem possa ser adequada para as condições padrão, ela não funciona quando sua caixa de texto já contém um pincel de fundo ou o fundo do formulário não é da mesma cor da caixa de texto.
LWChris
56

Esta é minha solução simples, adaptada da 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
fonte
Abordagem interessante, eu não teria pensado imediatamente em mim.
itsmatt de
agradável e simples :)
shmulik.r
3
Esta solução funciona especialmente bem se você definir o TextBlock comoIsHitTestVisible="False"
Mage Xy
1
@MageXy está se referindo ao primeiro TextBlock (o de dica).
Felix
Essa solução funciona muito bem se você quiser vincular o texto da dica a uma propriedade.
PeterB
11

que tal usar o materialDesign HintAssist? estou usando isso, e você também pode adicionar dica flutuante:

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

Eu instalei o Material Design com Pacote Nuget, há um guia de instalação no link de documentação

Mahdi Khalili
fonte
2
Biblioteca muito útil
AlexF11,
9

Faça isso no code-behind, definindo a cor do texto inicialmente para cinza e adicionando manipuladores de eventos para ganhar e perder o foco do teclado.

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

Em seguida, os manipuladores 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
fonte
7
-1 para fazer isso no code behind: desorganiza o controle, e há uma grande chance de que isso interfira com outra lógica de controle, se não agora, então no futuro.
AlexeiOst
4

Você pode fazer de uma forma muito simples. A ideia é colocar um rótulo no mesmo lugar da sua caixa de texto. Seu rótulo ficará visível se a caixa de texto não tiver texto e não tiver o 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" />

Bônus: Se você quiser ter um valor padrão para o seu textBox, certifique-se de defini-lo depois de enviar os dados (por exemplo: "InputText" = "PlaceHolder Text Here" se estiver vazio).

Makertoo
fonte
2

Outra abordagem ;-)

isso também funciona com PasswordBox. Se quiser usar com TextBox, basta trocar PasswordChangedcom 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 por trás:

private void txtPassword_PasswordChanged(object sender, RoutedEventArgs e)
{
    txtHintPassword.Visibility = Visibility.Visible;
    if (txtPassword.Password.Length > 0)
    {
        txtHintPassword.Visibility = Visibility.Hidden;
    }
}
Esteira
fonte
A melhor e mais simples solução que encontrei !! Obrigado!!
steve
2

Uma vez entrei na mesma situação, resolvi da seguinte maneira. Eu cumpri apenas os requisitos de uma caixa de dica, você pode torná-la mais interativa adicionando efeitos e outras coisas em outros eventos, como foco etc.

CÓDIGO WPF (removi o estilo para torná-lo legível)

<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
fonte
1

Outra solução é usar um kit de ferramentas WPF como MahApps.Metro. Possui muitos recursos interessantes, como uma marca d'água de caixa de texto:

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

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

StefanG
fonte
0

Usei os eventos de foco obtido e 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 bem, mas o texto ainda está cinza. Precisa de limpeza. Eu estava usando VB.NET

StarLordBlair
fonte
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
fonte
0

Essa é minha opinião:

<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>

A maioria das outras respostas, incluindo a primeira, tem falhas em minha opinião.

Esta solução funciona em todas as circunstâncias. XAML puro, facilmente reutilizável.

Julian
fonte
-1

I fazer isso com um VisualBrushe alguns gatilhos em um Stylesugerida por: 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: Aplicativo em execução, design bt não carregando ... vem o seguinte erro: Referência de tipo ambígua. Um tipo denominado 'StaticExtension' ocorre em pelo menos dois namespaces, 'MS.Internal.Metadata.ExposedTypes.Xaml' e 'System.Windows.Markup'. Considere ajustar os atributos de XmlnsDefinition do assembly. estou usando .net 3.5

SUHAIL AG
fonte
Resolvido o problema mudando <Trigger Property="Text" Value="{x:Static sys:String.Empty}">para<Trigger Property="Text" Value="">
SUHAIL AG
6
Esta parece ser uma resposta a outra postagem. Poste apenas soluções completas para a pergunta como respostas.
Drew Gaynor
4
Se você acha que a resposta de @sellmeadog está quase correta, considere corrigi-la, em vez de postar uma nova resposta quase sem diferença.
0xBADF00D
-11

Para WPF, não existe uma maneira. Você tem que imitar. Veja este exemplo . Uma solução secundária (solução fragmentada) é hospedar um controle de usuário WinForms que herda de TextBox e enviar a mensagem EM_SETCUEBANNER para o controle de edição. ie.

[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);
}

Além disso, se você deseja hospedar uma abordagem de controle WinForm, tenho uma estrutura que já inclui essa implementação chamada BitFlex Framework, que você pode baixar gratuitamente aqui .

Aqui está um artigo sobre o BitFlex se você quiser mais informações. Você começará a descobrir que se estiver procurando ter controles de estilo do Windows Explorer, isso geralmente nunca sai da caixa e, como o WPF não funciona com alças, geralmente você não pode escrever um wrapper fácil em torno do Win32 ou um controle existente como você pode com WinForms.

Captura de tela: insira a descrição da imagem aqui

David Anderson
fonte
1
uau, isso parece um pouco hack. Como posso fazer isso ao criar um controle de usuário com XAML?
Louis Rhys,
Você não. É assim que se faz. Se você quiser encapsular isso, crie um controle de usuário e uma propriedade CueText e chame SetCueText no setter.
David Anderson,
Eu acho que o OP deve hospedar controles winforms, para usar esta abordagem. Ou existe uma maneira de obter o identificador de caixa de texto?
localidade padrão
Isso parece algo que poderia ser feito declarativamente com WPF, vinculando se a caixa de texto tem foco ou não, etc. - O exemplo acima é mais uma abordagem WinForms - funcionará no WPF, mas não é o correto maneira.
BrainSlugs83
1
Desculpe, mas esta resposta está simplesmente incorreta. Além disso, todos os links estão quebrados.
Forest Kunecke