Comment puis-je ajouter un texte d'indication à la zone de texte WPF?


107

Par exemple, Facebook a un texte d'indication «Rechercher» dans la zone de texte Rechercher lorsque la zone de texte est vide.

Comment y parvenir avec les zones de texte WPF?

Zone de texte de recherche de Facebook


2
Essayez de rechercher «bannière de repère».
locale par défaut le

@MAKKAM cet article MSDN en discute mais il ne montre pas comment c'est fait
Louis Rhys


1
Je n'appellerais pas ce que vous demandez «texte d'indication». pour moi, le texte d'indication est une fenêtre contextuelle. Néanmoins, j'ai trouvé cette question en voulant configurer un texte d'espace réservé. et les réponses ci-dessous m'ont aidé.
steve

1
Cela s'appelle le filigrane au fait
Blechdose

Réponses:


158

Vous pouvez accomplir cela beaucoup plus facilement avec a VisualBrushet certains déclencheurs dans 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>

Pour augmenter la réutilisabilité de cela Style, vous pouvez également créer un ensemble de propriétés attachées pour contrôler le texte, la couleur, l'orientation, etc. de la bannière de repère.


1
Utilisez IsMouseCaptured au lieu de IsKeyboardFocused. C'est ainsi qu'une vraie bannière de repère répond.
Monstieur

5
Si quelqu'un se demandait comment utiliser les propriétés attachées pour augmenter la réutilisabilité du style, voir: stackoverflow.com/a/650620/724944
surfen

@Kurian IsMouseCaptured fera disparaître le repère uniquement lorsque vous cliquez dessus avec la souris, mais réapparaîtra lorsque vous relâcherez le bouton de la souris. Ça n'a pas l'air bien. IsMouseOver ne serait pas bon non plus (le clavier a le focus mais le pointeur de la souris est ailleurs => cue affiché). La plupart des bannières de repère utilisent IsKeyboardFocused (Facebook par exemple) et je pense que c'est bien. Une solution alternative serait d'utiliser les deux déclencheurs: (IsMouseOver OU IsKeyboardFocused)
surfen

23
La solution devrait être Hint = "Veuillez entrer votre texte" et non 20 éléments ... Hélas, cela n'est pas pris en charge par la légendaire zone de texte
intégrée

8
Bien que cette approche puisse convenir aux conditions par défaut, elle ne fonctionne pas lorsque votre zone de texte contient déjà un pinceau d'arrière-plan ou que l'arrière-plan du formulaire n'est pas de la même couleur que la zone de texte.
LWChris

56

C'est ma solution simple, adaptée 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>

Approche intéressante Je n'aurais pas pensé immédiatement à moi.
itsmatt

nice and simple :)
shmulik.r

3
Cette solution fonctionne particulièrement bien si vous définissez le TextBlock surIsHitTestVisible="False"
Mage Xy

1
@MageXy fait référence au premier TextBlock (celui de l'indice).
Felix

Cette solution fonctionne très bien si vous souhaitez lier le texte d'indication à une propriété.
PeterB

11

qu'en est-il de l'utilisation de materialDesign HintAssist? J'utilise ceci et vous pouvez également ajouter un indice flottant:

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

J'ai installé Material Design avec Nuget Package il y a un guide d'installation dans le lien de documentation


2
Bibliothèque très utile
AlexF11

9

Faites-le dans le code-behind en définissant initialement la couleur du texte sur gris et en ajoutant des gestionnaires d'événements pour gagner et perdre le focus clavier.

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

Puis les gestionnaires d'événements:

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";
        }
    }
}

7
-1 pour le faire dans le code derrière: encombre le contrôle, et il y a de fortes chances que cela interfère avec d'autres logiques de contrôle, sinon maintenant, alors dans le futur.
AlexeiOst ​​le


4

Vous pouvez le faire d'une manière très simple. L'idée est de placer une étiquette au même endroit que votre zone de texte. Votre étiquette sera visible si la zone de texte n'a pas de texte et n'a pas le focus.

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

Bonus: Si vous voulez avoir une valeur par défaut pour votre textBox, assurez-vous de la définir après la soumission des données (par exemple: "InputText" = "PlaceHolder Text Here" si vide).


2

Une autre approche ;-)

cela fonctionne également avec PasswordBox. Si vous souhaitez l'utiliser avec TextBox, échangez simplement PasswordChangedavec 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>

CodeBehind:

private void txtPassword_PasswordChanged(object sender, RoutedEventArgs e)
{
    txtHintPassword.Visibility = Visibility.Visible;
    if (txtPassword.Password.Length > 0)
    {
        txtHintPassword.Visibility = Visibility.Hidden;
    }
}

La meilleure et la plus simple solution que j'ai trouvée !! Merci!!
steve

2

Une fois, je me suis retrouvé dans la même situation, je l'ai résolu de la manière suivante. J'ai seulement rempli les conditions d'une boîte d'indices, vous pouvez la rendre plus interactive en ajoutant des effets et d'autres choses sur d'autres événements comme la mise au point, etc.

CODE WPF (j'ai supprimé le style pour le rendre lisible)

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

Code 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;
        }
    }

1

Une autre solution consiste à utiliser une boîte à outils WPF comme MahApps.Metro. Il a de nombreuses fonctionnalités intéressantes, comme un filigrane de zone de texte:

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

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


0

J'ai utilisé les événements de focus obtenu et perdu:

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

Cela fonctionne bien, mais le texte est toujours en gris. Besoin de nettoyage. J'utilisais VB.NET


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>

0

C'est mon avis:

<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 plupart des autres réponses, y compris la première, ont des défauts à mon avis.

Cette solution fonctionne en toutes circonstances. Pure XAML, facilement réutilisable.


-1

Je réalise cela avec un VisualBrushet certains déclencheurs dans une StyleSuggérée par: 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: Application en cours d'exécution, bt Design ne se chargeant pas ... l'erreur suivante vient: Référence de type ambiguë. Un type nommé «StaticExtension» apparaît dans au moins deux espaces de noms, «MS.Internal.Metadata.ExposedTypes.Xaml» et «System.Windows.Markup». Envisagez d'ajuster les attributs XmlnsDefinition de l'assembly. J'utilise .net 3.5


Résolu le problème en passant <Trigger Property="Text" Value="{x:Static sys:String.Empty}">à<Trigger Property="Text" Value="">
SUHAIL AG

6
Cela semble être une réponse à un autre message. Veuillez publier uniquement des solutions complètes à la question comme réponses.
Drew Gaynor le

4
Si vous pensez que la réponse de @ sellmeadog est presque correcte, pensez à la corriger, au lieu de publier une nouvelle réponse sans presque aucune différence.
0xBADF00D

-11

Pour WPF, il n'y a pas de moyen. Vous devez l'imiter. Voyez cet exemple . Une solution secondaire (floconneuse) consiste à héberger un contrôle utilisateur WinForms qui hérite de TextBox et à envoyer le message EM_SETCUEBANNER au contrôle d'édition. c'est à dire.

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

De plus, si vous souhaitez héberger une approche de contrôle WinForm, j'ai un framework qui inclut déjà cette implémentation appelée BitFlex Framework, que vous pouvez télécharger gratuitement ici .

Voici un article sur BitFlex si vous souhaitez plus d'informations. Vous commencerez à constater que si vous cherchez à avoir des contrôles de style Explorateur Windows, cela ne sort généralement jamais de la boîte, et parce que WPF ne fonctionne pas avec des poignées généralement, vous ne pouvez pas écrire un wrapper facile autour de Win32 ou un contrôle existant comme vous pouvez avec WinForms.

Capture d'écran: entrez la description de l'image ici


1
wow, cela semble un peu hackish .. Comment puis-je faire cela lors de la création d'un contrôle utilisateur avec XAML?
Louis Rhys

Vous ne le faites pas. Voilà comment cela se fait. Si vous souhaitez encapsuler cela, créez un contrôle utilisateur et une propriété CueText et appelez SetCueText dans le setter.
David Anderson

Je suppose que OP devrait héberger des contrôles winforms, pour utiliser cette approche. Ou existe-t-il un moyen d'obtenir une poignée de zone de texte?
locale par défaut le

Cela semble être quelque chose qui pourrait être fait de manière déclarative avec WPF, en se liant à savoir si la zone de texte a le focus ou non, etc. - L'exemple ci-dessus est plus une approche WinForms - cela fonctionnera dans WPF, mais ce n'est pas le bon façon.
BrainSlugs83

2
Désolé, mais cette réponse est tout simplement incorrecte. De plus, tous les liens sont rompus.
Forest Kunecke
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.