Answered by:
Watermark password box from my textbox

Question
-
Hi, I've got a nice(ish) design of a watermarked textbox. It's for a username box, but I also need one for password. Without having to completely redesign it how can I port it across to a password box:
WPF
<UserControl x:Class="prjP.Controls.ucWaterMarkedTextBox" x:Name="uc" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:prjP" mc:Ignorable="d" d:DesignHeight="22" d:DesignWidth="163" Focusable="True"> <UserControl.Resources> <ControlTemplate TargetType="TextBoxBase" x:Key="roundTransTxtB"> <Grid> <Border CornerRadius="2" BorderThickness="2" BorderBrush="#fd9733" x:Name="Bd" > <ScrollViewer Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" /> </Border> </Grid> <ControlTemplate.Triggers> <Trigger Property="UIElement.IsEnabled"> <Setter Property="Panel.Background" TargetName="Bd"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.ControlBrushKey}" /> </Setter.Value> </Setter> <Setter Property="TextElement.Foreground"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" /> </Setter.Value> </Setter> <Trigger.Value> <s:Boolean>False</s:Boolean> </Trigger.Value> </Trigger> <Trigger SourceName="PART_ContentHost" Property="IsFocused" Value="True"> <Setter TargetName="Bd" Property="BorderThickness" Value="5"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <local:TextInputToVisibilityConverter x:Key="TextInputToVisibilityConverter" /> </UserControl.Resources> <!-- <Border BorderBrush="Silver" BorderThickness="1" Height="Auto" Width="Auto" Name="bd"> </Border> --> <Grid Height="Auto" Width="Auto" Name="cv1" DataContext="{Binding ElementName=uc}"> <Grid.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="{Binding UpperColor, Mode=TwoWay}" Offset="0"/> <GradientStop Color="{Binding LowerColor, Mode=TwoWay}" Offset="1"/> </LinearGradientBrush> </Grid.Background> <TextBlock Foreground="White" Height="Auto" Name="tb1" Text="Watermark Text" Width="Auto" FontStyle="Italic" FontSize="12" Margin="3"> <TextBlock.Visibility> <MultiBinding Converter="{StaticResource TextInputToVisibilityConverter}"> <Binding ElementName="textBox1" Path="Text.IsEmpty" /> <Binding ElementName="textBox1" Path="IsFocused" /> </MultiBinding> </TextBlock.Visibility> </TextBlock> <TextBox Height="Auto" Name="textBox1" Width="Auto" Background="#00000000" Template="{DynamicResource roundTransTxtB}" Text="{Binding UserText}" TextChanged="textBox1_TextChanged" /> </Grid> </UserControl>
C# Code
public partial class ucWaterMarkedTextBox : UserControl { private string userText, waterMarkText; private bool password; public bool Password { get { return password; } set { password = value; } } private Color upperColor = Colors.LightGray, lowerColor = Colors.DarkGray; public Color UpperColor { get { return upperColor; } set { upperColor = Colors.Red; } } public Color LowerColor { get { return lowerColor; } set { lowerColor = value; } } public string WaterMarkText { get { return waterMarkText; } set { waterMarkText = value; tb1.Text = value; } } public string UserText { get { return userText; } set { userText = value; textBox1.Text = value; } } public ucWaterMarkedTextBox() { InitializeComponent(); } private void textBox1_TextChanged(object sender, TextChangedEventArgs e) { } } public class TextInputToVisibilityConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // Always test MultiValueConverter inputs for non-null // (to avoid crash bugs for views in the designer) if (values[0] is bool && values[1] is bool) { bool hasText = !(bool)values[0]; bool hasFocus = (bool)values[1]; if (hasFocus || hasText) return Visibility.Collapsed; } return Visibility.Visible; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
It's not like the old days when you could specify a password char, and I don't really want to do it in c# code around the current control... that doesn't seem like an elegant solution.
2010: Q6700 3GHz; 6GB DDRII; ~ 2.7TB internal; ASUS 512mb EN8800GT; Elixir Keyboard; Gigabyte GM-M6800; 2 x E173FPf 2005: 2GIG RAM, 3.6Ghz P4, 2 x 200gb SATA HD 8mb cache, 256mb 9950 ATI RADEON,19" LCDSunday, December 19, 2010 5:19 AM
Answers
-
OK Here's what I came up with based on Rao's style:
First we add a helper class in the prjP namespace: public static class AttachedProperties { public static string GetWatermark(DependencyObject obj) { return (string)obj.GetValue(WatermarkProperty); } public static void SetWatermark(DependencyObject obj, string value) { obj.SetValue(WatermarkProperty, value); } public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached("Watermark", typeof(string), typeof(AttachedProperties), new UIPropertyMetadata(string.Empty)); public static bool GetPasswordBoxHasText(DependencyObject obj) { return (bool)obj.GetValue(PasswordBoxHasTextProperty); } public static void SetPasswordBoxHasText(DependencyObject obj, bool value) { obj.SetValue(PasswordBoxHasTextProperty, value); } // Using a DependencyProperty as the backing store for PasswordBoxHasText. This enables animation, styling, binding, etc... public static readonly DependencyProperty PasswordBoxHasTextProperty = DependencyProperty.RegisterAttached("PasswordBoxHasText", typeof(bool), typeof(AttachedProperties), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnPasswordBoxHasTextPropertyChanged)); private static void OnPasswordBoxHasTextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { PasswordBox passwordBox = sender as PasswordBox; OnPasswordBoxHasTextChanged(passwordBox); passwordBox.PasswordChanged -= delegate { OnPasswordBoxHasTextChanged(passwordBox); }; passwordBox.PasswordChanged += delegate { OnPasswordBoxHasTextChanged(passwordBox); }; } private static void OnPasswordBoxHasTextChanged(PasswordBox box) { SetPasswordBoxHasText(box, !string.IsNullOrEmpty(box.Password)); } } Next the styles: <UserControl.Resources> <Style x:Key="styleWaterMarkedTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBoxBase}}"> <Setter Property="MinWidth" Value="268"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <Grid> <Border x:Name="BorderBase" CornerRadius="2" BorderThickness="2" BorderBrush="#fd9733" /> <Label x:Name="LabelPrompt" VerticalAlignment="Center" Content="{Binding Path=(local:AttachedProperties.Watermark), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}}" Visibility="Collapsed" Focusable="False" FontStyle="Italic" Padding="3,0,0,0"/> <ScrollViewer Margin="0" x:Name="PART_ContentHost" /> </Grid> <ControlTemplate.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Text" Value=""></Condition> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Visibility" TargetName="LabelPrompt" Value="Visible"></Setter> </MultiTrigger.Setters> </MultiTrigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Foreground" Value="Gray" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="styleWaterMarkedPasswordBox" TargetType="{x:Type PasswordBox}" BasedOn="{StaticResource {x:Type PasswordBox}}"> <Setter Property="MinWidth" Value="268"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type PasswordBox}"> <Grid> <Border x:Name="BorderBase" CornerRadius="2" BorderThickness="2" BorderBrush="#fd9733" /> <Label x:Name="LabelPrompt" VerticalAlignment="Center" Content="{Binding Path=(local:AttachedProperties.Watermark), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type PasswordBox}}}" Visibility="Collapsed" Focusable="False" FontStyle="Italic" Padding="3,0,0,0"/> <ScrollViewer Margin="0" x:Name="PART_ContentHost" /> </Grid> <ControlTemplate.Triggers> <Trigger Property="local:AttachedProperties.PasswordBoxHasText" Value="False"> <Setter Property="Visibility" TargetName="LabelPrompt" Value="Visible"></Setter> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Foreground" Value="Gray" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> And the usage: <TextBox Height="Auto" Name="textBox1" local:AttachedProperties.Watermark="Test Watermark" Width="200" Text="{Binding UserText}" Style="{StaticResource styleWaterMarkedTextBox}"/> <PasswordBox Grid.Row="1" local:AttachedProperties.Watermark="Test Password" local:AttachedProperties.PasswordBoxHasText="True" Height="Auto" Width="200" Margin="0,5,0,0" Style="{StaticResource styleWaterMarkedPasswordBox}" />
Note: setting local:AttachedProperties.PasswordBoxHasText="True" is required to setup the event.
You'll need to clean these up to match your style, but they should work for you.
John Fenton
Wordmasters Direct Mail and Data Processing ServicesTuesday, December 21, 2010 7:01 PM
All replies
-
Someone...
2010: Q6700 3GHz; 6GB DDRII; ~ 2.7TB internal; ATI RAEDON 5770 1GB @ Stock; Elixir Keyboard; Gigabyte GM-M6800; 2 x E173FPf 2005: 2GIG RAM, 3.6Ghz P4, 2 x 200gb SATA HD 8mb cache, 256mb 9950 ATI RADEON,19" LCD Core i7 3.2GHz, 6GB DDRIII, ASUS 512MB EN8800GTTuesday, December 21, 2010 3:24 AM -
Hi,
try out this
<Style x:Key="WaterMarkTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBoxBase}}"> <Setter Property="MinWidth" Value="268"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <Grid> <Border x:Name="BorderBase" BorderThickness="1" BorderBrush="{DynamicResource ControlBorderBrush}" Background="{DynamicResource ControlBrush}" CornerRadius="2" /> <Label x:Name="LabelPrompt" Content=" Search Here" Visibility="Collapsed" Focusable="False" FontStyle="Italic" Padding="3,0,0,0"/> <ScrollViewer Margin="0" x:Name="PART_ContentHost" /> </Grid> <ControlTemplate.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Text" Value=""></Condition> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Visibility" TargetName="LabelPrompt" Value="Visible"></Setter> </MultiTrigger.Setters> </MultiTrigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Foreground" Value="Gray" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
hope this helps u- Proposed as answer by Wodahs Thursday, December 23, 2010 2:33 AM
Tuesday, December 21, 2010 5:21 AM -
I like Rao's method a lot better than your original design.
There is however a problem with watermarking a Passwordbox.
Password is not a DependancyProperty so it cannot be bound to or used as a Trigger.
I'll try and write something for you if someone else doesn't get to it first, but it may take me a day or two.
The trick here is to use some attached properties.
John Fenton
Wordmasters Direct Mail and Data Processing ServicesTuesday, December 21, 2010 7:48 AM -
OK Here's what I came up with based on Rao's style:
First we add a helper class in the prjP namespace: public static class AttachedProperties { public static string GetWatermark(DependencyObject obj) { return (string)obj.GetValue(WatermarkProperty); } public static void SetWatermark(DependencyObject obj, string value) { obj.SetValue(WatermarkProperty, value); } public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached("Watermark", typeof(string), typeof(AttachedProperties), new UIPropertyMetadata(string.Empty)); public static bool GetPasswordBoxHasText(DependencyObject obj) { return (bool)obj.GetValue(PasswordBoxHasTextProperty); } public static void SetPasswordBoxHasText(DependencyObject obj, bool value) { obj.SetValue(PasswordBoxHasTextProperty, value); } // Using a DependencyProperty as the backing store for PasswordBoxHasText. This enables animation, styling, binding, etc... public static readonly DependencyProperty PasswordBoxHasTextProperty = DependencyProperty.RegisterAttached("PasswordBoxHasText", typeof(bool), typeof(AttachedProperties), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnPasswordBoxHasTextPropertyChanged)); private static void OnPasswordBoxHasTextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { PasswordBox passwordBox = sender as PasswordBox; OnPasswordBoxHasTextChanged(passwordBox); passwordBox.PasswordChanged -= delegate { OnPasswordBoxHasTextChanged(passwordBox); }; passwordBox.PasswordChanged += delegate { OnPasswordBoxHasTextChanged(passwordBox); }; } private static void OnPasswordBoxHasTextChanged(PasswordBox box) { SetPasswordBoxHasText(box, !string.IsNullOrEmpty(box.Password)); } } Next the styles: <UserControl.Resources> <Style x:Key="styleWaterMarkedTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBoxBase}}"> <Setter Property="MinWidth" Value="268"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <Grid> <Border x:Name="BorderBase" CornerRadius="2" BorderThickness="2" BorderBrush="#fd9733" /> <Label x:Name="LabelPrompt" VerticalAlignment="Center" Content="{Binding Path=(local:AttachedProperties.Watermark), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}}" Visibility="Collapsed" Focusable="False" FontStyle="Italic" Padding="3,0,0,0"/> <ScrollViewer Margin="0" x:Name="PART_ContentHost" /> </Grid> <ControlTemplate.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Text" Value=""></Condition> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Visibility" TargetName="LabelPrompt" Value="Visible"></Setter> </MultiTrigger.Setters> </MultiTrigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Foreground" Value="Gray" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="styleWaterMarkedPasswordBox" TargetType="{x:Type PasswordBox}" BasedOn="{StaticResource {x:Type PasswordBox}}"> <Setter Property="MinWidth" Value="268"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type PasswordBox}"> <Grid> <Border x:Name="BorderBase" CornerRadius="2" BorderThickness="2" BorderBrush="#fd9733" /> <Label x:Name="LabelPrompt" VerticalAlignment="Center" Content="{Binding Path=(local:AttachedProperties.Watermark), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type PasswordBox}}}" Visibility="Collapsed" Focusable="False" FontStyle="Italic" Padding="3,0,0,0"/> <ScrollViewer Margin="0" x:Name="PART_ContentHost" /> </Grid> <ControlTemplate.Triggers> <Trigger Property="local:AttachedProperties.PasswordBoxHasText" Value="False"> <Setter Property="Visibility" TargetName="LabelPrompt" Value="Visible"></Setter> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Foreground" Value="Gray" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> And the usage: <TextBox Height="Auto" Name="textBox1" local:AttachedProperties.Watermark="Test Watermark" Width="200" Text="{Binding UserText}" Style="{StaticResource styleWaterMarkedTextBox}"/> <PasswordBox Grid.Row="1" local:AttachedProperties.Watermark="Test Password" local:AttachedProperties.PasswordBoxHasText="True" Height="Auto" Width="200" Margin="0,5,0,0" Style="{StaticResource styleWaterMarkedPasswordBox}" />
Note: setting local:AttachedProperties.PasswordBoxHasText="True" is required to setup the event.
You'll need to clean these up to match your style, but they should work for you.
John Fenton
Wordmasters Direct Mail and Data Processing ServicesTuesday, December 21, 2010 7:01 PM