Pull to refresh

Watermark для TextBox-а в Windows 8 приложениях

Reading time4 min
Views6.5K
В последнее время, стало модным, помимо поясняющей надписи для TextBox-а еще и на самом TextBox-е писать некоторую подсказку. Должно все это выглядеть так, как на картинке для привлечения внимания. Пока пользователь ничего не ввел, подсвечивается подсказка. Если пользователь ввел текст, то подсказка не показывается. Не бог весть, какая сложная логика, но т.к. у ControlTemplate в Windows Store приложениях отобрали триггеры, то придется это делать не стилем, а именно новым элементом управления.
Под катом, будем пошаговое руководство, по разработке такого компонента, ориентированное на тех, кто первый раз подходит к разработке своих элементов управления. Для тех, кто уже программирует под Windows Store откровений не будет, но если не сложно, выложите рекомендации в комментариях, может кто-то действительно решит сделать хороший элемент управления для такой задачки.

Поехали.
Создаем новое Windows Store приложение. В Solution добавляем новую сборку (правый клик мыши на Solution, в контекстном меню Add -> New Project):

В эту сборку добавляем новый элемент (правый клик на проекте, Add -> New Item):

В файле cs нашего контрола, меняем предка на TextBox:
public sealed partial class WaterMarkedTextBox : TextBox

Также, нам придется поменять разметку, заменив базовый компонент и удалив содержимое:

Эти нехитрые действия, позволили нам получить в своем компоненте все поля и методы, которые были у базового TextBox-а. Единственно, что нас не устраивает, это отсутствие у TextBox-а возможности показывать подсказку. Переопределим шаблон нашего потомка TextBox-а, добавив в него TextBlock, для отображения подсказки:
<TextBox.Template>
    <ControlTemplate TargetType="TextBox">
        <Grid>
            <TextBox Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" />
            <TextBlock 
                Text="{Binding WaterMark, RelativeSource={RelativeSource TemplatedParent}}"
                Foreground="Gray" Margin="5,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" Visibility="Collapsed" IsHitTestVisible="False"/>
        </Grid>
    </ControlTemplate>
</TextBox.Template>

Т.к. в конструкторе работать с визуальными компонентами еще нельзя, то подпишемся на событие загрузки компонента и всю полезную работу будем выполнять в обработчике этого события:

public WaterMarkedTextBox()
{
    this.InitializeComponent();            
    Loaded += WaterMarkedTextBox_Loaded;
}

Для определения, есть у нас фокус на элементе или нет, добавим поле:

private bool _isFocused;

Ну и нам понадобиться DependencyProperty, для хранения текста подсказки:

/// <summary>
/// Подсказка показываемая пользователю
/// </summary>
public string WaterMark
{
    get { return (string)GetValue(WaterMarkProperty); }
    set { SetValue(WaterMarkProperty, value); }
}
 
/// <summary>
/// Static part of dependency property WaterMark
/// </summary>
public static readonly DependencyProperty WaterMarkProperty =
    DependencyProperty.Register("WaterMark", typeof(string), typeof(WaterMarkedTextBox), new PropertyMetadata(""));

Все, пишем обработку загрузки компонента, получения фокуса ввода и потери его:

void WaterMarkedTextBox_Loaded(object sender, RoutedEventArgs e)
{
    var grid = (Grid)VisualTreeHelper.GetChild(this, 0);
    TextBox innerTextBox = (TextBox)grid.Children[0];
    innerTextBox.GotFocus += WaterMarkedTextBox_GotFocus;
    innerTextBox.LostFocus += WaterMarkedTextBox_LostFocus;
    ChangeWatermarkTextVisibility();
}
 
void WaterMarkedTextBox_LostFocus(object sender, RoutedEventArgs e)
{
    _isFocused = false;
    ChangeWatermarkTextVisibility();
}
 
void WaterMarkedTextBox_GotFocus(object sender, RoutedEventArgs e)
{
    _isFocused = true;
    ChangeWatermarkTextVisibility();
}

Ну и самый последний метод, для изменения видимости подсказки:

private void ChangeWatermarkTextVisibility()
{
    var grid = (Grid)VisualTreeHelper.GetChild(this, 0);
    TextBlock watermarkText = (TextBlock)grid.Children[1];
    if (!string.IsNullOrEmpty(Text) || _isFocused)
    {
        watermarkText.Visibility = Visibility.Collapsed;
    }
    else
    {
        watermarkText.Visibility = Visibility.Visible;
    }
}

Собираем решение.
Т.к. я в качестве исходного проекта взял Blank App, то на главную форму внес вот такую разметку:

<Page x:Class="App11.MainPage"
      
    xmlns:MyControls="using:MyControls" 
    
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App11"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
 
    <StackPanel Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Укажите сотрудника:" />
        <MyControls:WaterMarkedTextBox WaterMark="Введите часть ФИО и нажмите Enter" />
    </StackPanel>
</Page>

Чтобы не думать о подключении namespace, я обычно новые компоненты перетягиваю на форму из панели Tools:

Ну а как все это выглядит, можно посмотреть в первой картинке этого поста, ну или повторив мои действия и запустив приложение.
Tags:
Hubs:
Total votes 10: ↑7 and ↓3+4
Comments20

Articles