Pokud se v Silverlight koukneme na kontrol TextBox, zjistíme, že již obsahuje vlastnost Watermark, ta je však bohužel implementována takto:
throw new NotImplementedException();
|
Pojďme to udělat lépe.
Zavedeme kontrol ExtendedTextBox poděděný z TextBox kontrolu, a v něm založíme novou (pomoci klíčového slova new) dependency property Watermark.
/// <summary>
/// Watermark dependency property
/// </summary>
public new static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register( "Watermark" , typeof ( object ), typeof (ExtendedTextBox), new PropertyMetadata(OnWatermarkPropertyChanged));
/// <summary>
/// Watermark content
/// </summary>
/// <value>The watermark.</value>
public new object Watermark
{
get { return ( object )GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value ); }
}
|
V kontrolu dále zavedeme nové stavy pro VisualStateManager - Watermarked a Unwatermarked . Tyto stavy budeme přepínat na událost TextChanged pomoci metody GoToState.
/// <summary>
/// ExtendedTextBox control constructor
/// </summary>
public ExtendedTextBox()
{
this .DefaultStyleKey = typeof (ExtendedTextBox);
this .TextChanged += OnTextChanged;
}
private void OnTextChanged( object sender, TextChangedEventArgs e)
{
SetWatermarkState( true );
}
private void SetWatermarkState( bool useTransitions)
{
if ( this .Watermark != null && string .IsNullOrEmpty( this .Text))
{
GoToState( this , useTransitions, "Watermarked" , "Unwatermarked" );
}
else
{
GoToState( this , useTransitions, "Unwatermarked" );
}
}
private static void GoToState(Control control, bool useTransitions, params string [] stateNames)
{
if (control == null )
{
throw new ArgumentNullException( "control" );
}
if (stateNames != null )
{
foreach ( string str in stateNames)
{
if (VisualStateManager.GoToState(control, str, useTransitions))
{
return ;
}
}
}
}
|
Poslední částí řešení bude definovat nový styl pro náš kontrol ExtendedTextBox. Vyjdeme ze standardního stylu pro TextBox, rozšíříme ho o nový VisualStateGroup WatermarkStates a pod ContentElement přidáme ContentControl Watermark.
< Setter Property = "Template" >
< Setter.Value >
< ControlTemplate TargetType = "impcontrols:ExtendedTextBox" >
< Grid x:Name = "RootElement" >
< VisualStateManager.VisualStateGroups >
...
< VisualStateGroup x:Name = "WatermarkStates" >
< VisualStateGroup.Transitions >
< VisualTransition GeneratedDuration = "0" />
</ VisualStateGroup.Transitions >
< VisualState x:Name = "Unwatermarked" />
< VisualState x:Name = "Watermarked" >
< Storyboard >
< DoubleAnimation Storyboard.TargetName = "Watermark" Storyboard.TargetProperty = "Opacity" To = "1" Duration = "0" />
</ Storyboard >
</ VisualState >
</ VisualStateGroup >
</ VisualStateManager.VisualStateGroups >
< Border x:Name = "Border" BorderBrush = "{TemplateBinding BorderBrush}" BorderThickness = "{TemplateBinding BorderThickness}" Background = "{TemplateBinding Background}" CornerRadius = "1" Opacity = "1" >
< Grid >
< Border x:Name = "ReadOnlyVisualElement" Background = "#5EC9C9C9" Opacity = "0" />
< Border x:Name = "MouseOverBorder" BorderBrush = "Transparent" BorderThickness = "1" >
< ScrollViewer x:Name = "ContentElement" BorderThickness = "0" IsTabStop = "False" Padding = "{TemplateBinding Padding}" />
</ Border >
< ContentControl x:Name = "Watermark" Opacity = "0" IsTabStop = "False" IsHitTestVisible = "False" Content = "{TemplateBinding Watermark}" Foreground = "#FF999999" Background = "{TemplateBinding Background}" FontFamily = "{TemplateBinding FontFamily}" FontSize = "{TemplateBinding FontSize}" FontStretch = "{TemplateBinding FontStretch}" FontStyle = "{TemplateBinding FontStyle}" FontWeight = "{TemplateBinding FontWeight}" Margin = "{TemplateBinding Padding}" VerticalAlignment = "Top" />
</ Grid >
</ Border >
...
</ Grid >
</ ControlTemplate >
</ Setter.Value >
</ Setter >
|
Celý kód kontrolu (obsahuje i změny z minula) je dostupný zde ExtendedTextBox.cs a jeho styl zde ExtendedTextBox.xaml.
Obdobně by bylo možné toto řešení převzít i do WPF.
Pokud chceme watermark dodat i pro kontrol PasswordBox, je zde drobný problém v tom, že kontrol PasswordBox je v Silverlight implementován jako sealed, a tak ho nemůžeme podědit. Budeme postupovat jinak. Vlastnost Watermark pro tento kontrol zavedeme jako attached dependency property v nové třídě, kterou nazveme PasswordBoxExtender. Implementace je jinak velice podobná jako v případě TextBoxu. Třídu si můžete prohlédnou zde PasswordBoxExtender.cs.
Také obdobně vytvoříme nový styl pro PasswordBox, ve kterém ale musíme jako Content přidávaného kontrolu ContentControl Watermarku použít odkaz na attached vlastnost:
Content="{TemplateBinding local:PasswordBoxExtender.Watermark}"
Styl je opět dostupný zde PasswordBoxStyles.xaml.