Сабж
На самом деле очень тревиальная задача, но, столкнувшись с которой, можно потерять драгоценное время.
Итак, что мы имеем:
WinRT XAML Toolkit несет на своем борту Watermark TextBox, но имеет ограниченый функционал (цветовое оформление Watermarkа). Вдобавок отсутсвие Watermark PasswordBox огорчает.
Следуйщий текст позволит Вам сэкономить 30 минут времении, имплементировав и настроив парочку простеньких контролов). Если интересно — добро пожаловать под кат.
Идея
Идея крайне проста: TextBox для ввода текста и TextBlock для отображения Watermark. (PasswordBox и TextBlock соответсвенно). Осталось только хитро манипулировать с прозрачностью контролов.
Представленный пример предназначен для Windows 8/RT. Имплементировать для Windows Phone не составит труда.
WatermarkTextBox.xaml
<UserControl
x:Class="Test.Controls.WatermarkTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="50"
d:DesignWidth="400">
<Border x:Name="brd" Background="#FFE0E0E0" VerticalAlignment="Stretch" Padding="15,5" >
<Grid>
<TextBlock x:Name="tbWatermark" TextWrapping="Wrap"
Foreground="#FF646464" IsHitTestVisible="False" Padding="0"
FontSize="14.667" VerticalAlignment="Center" Margin="0" />
<TextBox x:Name="tb" LostFocus="LostFocus" GotFocus="GotFocus" Background="{x:Null}" FontSize="11" TextChanged="Tb_OnTextChanged"
Margin="0" VerticalAlignment="Center" BorderThickness="0" Padding="0"
/>
</Grid>
</Border>
</UserControl>
WatermarkTextBox.cs
public sealed partial class WatermarkTextBox : UserControl
{
public WatermarkTextBox()
{
InitializeComponent();
}
private void LostFocus(object sender, RoutedEventArgs e)
{
CheckWatermark();
brd.Background = new SolidColorBrush(Color.FromArgb(255, 224, 224, 224));
}
public void CheckWatermark()
{
var passwordEmpty = string.IsNullOrEmpty(tb.Text);
tbWatermark.Opacity = passwordEmpty ? 100 : 0;
tb.Opacity = passwordEmpty ? 0 : 100;
}
private void GotFocus(object sender, RoutedEventArgs e)
{
tbWatermark.Opacity = 0;
tb.Opacity = 100;
brd.Background = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));
}
private void Tb_OnTextChanged(object sender, TextChangedEventArgs e)
{
Text = tb.Text;
}
#region DependencyProperty
/// <summary>
/// Watermark
/// </summary>
public string Watermark
{
get { return (string)GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(string), typeof(WatermarkTextBox), new PropertyMetadata("", WatermarkChanged));
private static void WatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var controll = (WatermarkTextBox)d;
var val = (string)e.NewValue;
controll.tbWatermark.Text = val;
}
/// <summary>
/// TextSize
/// </summary>
public int TextSize
{
get { return (int)GetValue(TextSizeProperty); }
set { SetValue(TextSizeProperty, value); }
}
public static readonly DependencyProperty TextSizeProperty = DependencyProperty.Register("TextSize", typeof(int), typeof(WatermarkTextBox), new PropertyMetadata(0, TextSizeChanged));
private static void TextSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var controll = (WatermarkTextBox)d;
var val = (int)e.NewValue;
if (val < 10){val = 10;}
controll.tb.FontSize = val;
controll.tbWatermark.FontSize = val;
}
/// <summary>
/// Text
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(WatermarkTextBox), new PropertyMetadata("", TextChanged));
private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var controll = (WatermarkTextBox)d;
var val = (string)e.NewValue;
if (val == null)
{
controll.tb.Text = "";
return;
}
controll.tb.Text = val;
}
#endregion
}
WatermarkPasswordBox.xaml
UserControl
x:Class="Test.Controls.WatermarkPasswordBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Border x:Name="brd"
Background="#FFE0E0E0" VerticalAlignment="Stretch" Padding="15,5">
<Grid>
<TextBlock x:Name="tbWatermark" TextWrapping="Wrap"
Foreground="#FF646464" IsHitTestVisible="False" Padding="0"
FontSize="14.667" VerticalAlignment="Center" Margin="0" />
<PasswordBox x:Name="pb" LostFocus="PasswordLostFocus" GotFocus="PasswordGotFocus"
Background="{x:Null}" FontSize="11" PasswordChanged="Pb_OnPasswordChanged"
Margin="0" VerticalAlignment="Center" BorderThickness="0" Opacity="0" Padding="0" />
</Grid>
</Border>
</UserControl>
WatermarkPasswordBox.cs
public sealed partial class WatermarkPasswordBox : UserControl
{
public WatermarkPasswordBox()
{
this.InitializeComponent();
}
private void PasswordLostFocus(object sender, RoutedEventArgs e)
{
CheckWatermark();
brd.Background = new SolidColorBrush(Color.FromArgb(255, 224, 224, 224));
}
public void CheckWatermark()
{
var passwordEmpty = string.IsNullOrEmpty(pb.Password);
tbWatermark.Opacity = passwordEmpty ? 100 : 0;
pb.Opacity = passwordEmpty ? 0 : 100;
}
private void PasswordGotFocus(object sender, RoutedEventArgs e)
{
tbWatermark.Opacity = 0;
pb.Opacity = 100;
brd.Background = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));
}
private void Pb_OnPasswordChanged(object sender, RoutedEventArgs e)
{
Password = pb.Password;
}
#region DependencyProperty
/// <summary>
/// Watermark
/// </summary>
public string Watermark
{
get { return (string)GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(string), typeof(WatermarkPasswordBox), new PropertyMetadata("", WatermarkChanged));
private static void WatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var controll = (WatermarkPasswordBox)d;
var val = (string)e.NewValue;
controll.tbWatermark.Text = val;
}
/// <summary>
/// Password
/// </summary>
public string Password
{
get { return (string)GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(string), typeof(WatermarkPasswordBox), new PropertyMetadata("", PasswordChanged));
private static void PasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var controll = (WatermarkPasswordBox)d;
var val = (string)e.NewValue;
if (val == null)
{
controll.pb.Password = "";
return;
}
controll.pb.Password = val;
}
/// <summary>
/// TextSize
/// </summary>
public int TextSize
{
get { return (int)GetValue(TextSizeProperty); }
set { SetValue(TextSizeProperty, value); }
}
public static readonly DependencyProperty TextSizeProperty = DependencyProperty.Register("TextSize", typeof(int), typeof(WatermarkPasswordBox), new PropertyMetadata(0, TextSizeChanged));
private static void TextSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var controll = (WatermarkPasswordBox)d;
var val = (int)e.NewValue;
if (val < 10) { val = 10; }
controll.pb.FontSize = val;
controll.tbWatermark.FontSize = val;
}
#endregion
}
И простое использование в любом нужном месте:
<controls:WatermarkTextBox x:Name="WatermarkTextBox " Text="{Binding Login, Mode=TwoWay}" Watermark="Login"
Grid.Row="2" Margin="10,0" TextSize="18"/>
<controls:WatermarkPasswordBox x:Name="WatermarkPasswordBox" Watermark="Password"
Password="{Binding Password, Mode=TwoWay}" Grid.Row="4" Margin="10,0" TextSize="18" />
Только не забываем любимую строчку:
xmlns:controls="using:Test.Controls"
Как это работает?
При получении GotFocus/LostFocus обрабатываются при начале/окончании ввода текста/пароля. На основании введенных значений делается выбор об отображении Текста либо Watermark (метод CheckWatermark). При вводе текста/пароля в соответсующее поле перенаправляем в наше созданную DependencyProperty (методы Tb_OnTextChanged/Pb_OnTextChanged). Небольшой список DependencyProperty включает в себя текст Watermark, текст основного поля ввода, размер шрифта метки и текста.
Продолжаем подстраивать под себя
Совсем не сложно добавить любое необходимое свойство по Вашему вкусу. Потратили 10 минут, но получили полный контроль. Надеюсь сэкономил кому-нибудь время.
Note
Во время написания статьи наткнулся на Dependency Property Generator. Надеюсь это тоже будет экономить время.
Объективная/необъективная критика приветсвуется.
Автор: jimpanzer