Как добавить текст подсказки в текстовое поле WPF?

107

Например, Facebook имеет текст подсказки «Поиск» в текстовом поле «Поиск», когда текстовое поле пусто.

Как добиться этого с помощью текстовых полей WPF ??

Текстовое поле поиска Facebook

Луи Рис
источник
2
Попробуйте выполнить поиск по запросу "cue banner".
локаль по умолчанию
@MAKKAM, эта статья MSDN обсуждает это, но не показывает, как это делается
Луи Рис,
1
Я бы не назвал то, что вы просите, «подсказкой». для меня текст подсказки - это всплывающее окно. тем не менее я нашел этот вопрос, когда хотел настроить текст заполнителя. и ответы ниже помогли мне.
Стив
1
Кстати, это называется водяным знаком
Blechdose

Ответы:

158

Вы можете сделать это намного проще с помощью a VisualBrushи некоторых триггеров в 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>

Чтобы повысить возможность повторного использования этого Style, вы также можете создать набор прикрепленных свойств для управления фактическим текстом, цветом, ориентацией баннера cue и т. Д.

продавать
источник
1
Используйте IsMouseCaptured вместо IsKeyboardFocused. Так реагирует настоящий баннер с подсказкой.
Monstieur
5
Если кому-то интересно, как использовать присоединенные свойства для увеличения повторного использования стилей, см .: stackoverflow.com/a/650620/724944
surfen
@Kurian IsMouseCaptured заставит реплику исчезнуть, только когда вы щелкнете ее мышью, но появится снова, когда вы отпустите кнопку мыши. Это не выглядит хорошо. IsMouseOver тоже не годится (клавиатура имеет фокус, а указатель мыши находится где-то в другом месте => отображается подсказка). Большинство баннеров cue используют IsKeyboardFocused (например, Facebook), и я думаю, что это нормально. Альтернативным решением было бы использовать оба триггера: (IsMouseOver ИЛИ IsKeyboardFocused)
surfen
23
Решение должно быть Hint = "Пожалуйста, введите текст", а не 20 элементов ... Увы, это не поддерживается легендарным встроенным текстовым полем ...
Mzn
8
Хотя этот подход может быть приемлемым для условий по умолчанию, он не работает, если ваше текстовое поле уже содержит фоновую кисть или фон формы не того же цвета, что и текстовое поле.
LWChris
56

Это мое простое решение, адаптированное от 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>
Мартин Шмидт
источник
Интересный подход Я бы не подумал сразу.
itsmatt
красиво и просто :)
shmulik.r
3
Это решение работает особенно хорошо, если вы установите для TextBlock значениеIsHitTestVisible="False"
Mage Xy
1
@MageXy относится к первому TextBlock (подсказка).
Феликс
Это решение отлично работает, если вы хотите привязать текст подсказки к свойству.
PeterB
11

как насчет использования materialDesign HintAssist? Я использую это, вы также можете добавить плавающую подсказку:

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

Я установил Material Design с пакетом Nuget, в ссылке документации есть руководство по установке

Махди Халили
источник
2
Очень полезная библиотека
AlexF11 07
9

Сделайте это в коде программной части, установив изначально серый цвет текста и добавив обработчики событий для получения и потери фокуса клавиатуры.

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

Затем обработчики событий:

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
источник
7
-1 за выполнение этого в коде позади: загромождает элемент управления, и есть высокая вероятность того, что это будет мешать другой логике управления, если не сейчас, то в будущем.
AlexeiOst ​​07
5

Вы должны создать настраиваемый элемент управления, унаследовав текстовое поле. Ссылка ниже содержит отличный пример образца текстового поля поиска. Пожалуйста, взгляните на это

http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/

Кишор Кумар
источник
2
Ответы, содержащие только ссылки, не приветствуются
Джош Ноу,
4

Вы можете сделать это очень просто. Идея состоит в том, чтобы разместить метку в том же месте, что и текстовое поле. Ваша метка будет видна, если текстовое поле не имеет текста и не в фокусе.

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

Бонус: если вы хотите иметь значение по умолчанию для вашего текстового поля, обязательно установите его при отправке данных (например: "InputText" = "PlaceHolder Text Here", если пусто).

производитель
источник
2

Другой подход ;-)

это также работает с PasswordBox. Если вы хотите использовать его с TextBoxпросто обмениваться PasswordChangedс 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;
    }
}
Мат
источник
Лучшее и простое решение, которое я нашел !! Спасибо!!
Стив
2

Однажды я попал в такую ​​же ситуацию, решил ее следующим образом. Я выполнил только требования к окну подсказок, вы можете сделать его более интерактивным, добавив эффекты и другие вещи к другим событиям, таким как фокус и т. Д.

КОД WPF (я удалил стили, чтобы сделать его читабельным)

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

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

Другое решение - использовать набор инструментов WPF, например MahApps.Metro. Он имеет множество приятных функций, таких как водяной знак текстового поля:

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

См. Http://mahapps.com/controls/textbox.html

StefanG
источник
0

Я использовал события получения и потери фокуса:

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

Работает хорошо, но текст по-прежнему остается серым. Требуется уборка. Я использовал VB.NET

StarLordBlair
источник
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
источник
0

Это мое мнение:

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

На мой взгляд, большинство других ответов, включая верхний, имеют недостатки.

Это решение работает при любых обстоятельствах. Чистый XAML, легко повторно используемый.

Юлиан
источник
-1

Я совершаю это с VisualBrushи некоторые триггеры в Styleпредложенных: 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: приложение работает, bt Design не загружается ... появляется следующая ошибка: неоднозначная ссылка на тип. Тип с именем «StaticExtension» встречается как минимум в двух пространствах имен: «MS.Internal.Metadata.ExposedTypes.Xaml» и «System.Windows.Markup». Рассмотрите возможность настройки атрибутов сборки XmlnsDefinition. Я использую .net 3.5

СУХАЙЛ АГ
источник
Решил проблему, изменив <Trigger Property="Text" Value="{x:Static sys:String.Empty}">на<Trigger Property="Text" Value="">
SUHAIL AG
6
Похоже, это ответ на другой пост. Пожалуйста, публикуйте только полные решения вопроса в качестве ответов.
Дрю Гейнор
4
Если вы считаете, что ответ @ sellmeadog почти правильный, подумайте о его исправлении, вместо того, чтобы публиковать новый ответ почти без разницы.
0xBADF00D
-11

Для WPF выхода нет. Вы должны имитировать это. См. Этот пример . Второстепенное (нестабильное решение) - разместить пользовательский элемент управления WinForms, который наследуется от TextBox, и отправить сообщение EM_SETCUEBANNER элементу редактирования. т.е.

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

Кроме того, если вы хотите использовать подход управления WinForm, у меня есть инфраструктура, которая уже включает эту реализацию под названием BitFlex Framework, которую вы можете бесплатно скачать здесь .

Вот статья о BitFlex, если вам нужна дополнительная информация. Вы начнете обнаруживать, что если вы хотите иметь элементы управления в стиле Windows Explorer, которые обычно никогда не выходят из коробки, и поскольку WPF не работает с дескрипторами, вы не можете написать простую оболочку для Win32 или существующего элемента управления, как вы можете с WinForms.

Скриншот: введите описание изображения здесь

Дэвид Андерсон
источник
1
вау, это выглядит немного хакерским ... Как я могу это сделать при создании пользовательского элемента управления с помощью XAML?
Луи Рис,
Вы этого не сделаете. Вот как это делается. Если вы хотите инкапсулировать это, создайте пользовательский элемент управления и свойство CueText и вызовите SetCueText в установщике.
Дэвид Андерсон
Я думаю, OP должен содержать элементы управления winforms, чтобы использовать этот подход. Или есть способ получить дескриптор текстового поля?
локаль по умолчанию
Это похоже на то, что можно сделать декларативно с WPF, привязав его к тому, имеет ли текстовое поле фокус или нет, и т. Д. - Приведенный выше пример больше похож на подход WinForms - он будет работать в WPF, но это неправильно путь.
BrainSlugs83
2
Извините, но это просто неверный ответ. Также все ссылки не работают.
Forest Kunecke