How do I implement the light gray background text in a TextBox?  
Author Message
Art_Sherwood





PostPosted: Windows Presentation Foundation (WPF), How do I implement the light gray background text in a TextBox? Top

Windows Vista has lots of places where text boxes show a light gray info message on the background telling the user what they can enter in that box. The message disappears as soon as the user types something in the box. For example, the search box in explorer windows demonstrates this functionality. The Windows UX guidelines talk about this as well. How does one go about implementing this functionality in Avalon Is there a way to modify the TextBoxBase template perhaps


Visual Studio 200827  
 
 
Drew Marsh





PostPosted: Windows Presentation Foundation (WPF), How do I implement the light gray background text in a TextBox? Top

Hook the GotKeyboardFocus and LostKeyboardFocus events of the text box. On focus you need to remove your "enter search" text and when you lose it, if the text box was left empty, you put it back. Obviously you can also do things like change the style of the text as well if the UX guidelines say it should look diff. when the "enter search" text there vs. a real value entered by the user.

HTH,
Drew


 
 
footballism





PostPosted: Windows Presentation Foundation (WPF), How do I implement the light gray background text in a TextBox? Top

I wrote an article on my personal blog talking about how to implement such kinda functionality (aka in place editing) in WPF, you can find it here

Sheva


 
 
footballism





PostPosted: Windows Presentation Foundation (WPF), How do I implement the light gray background text in a TextBox? Top

After closer examination, I think Drew MarshNash's solution should be more appropriate in this scenario, so I rewrite the InfoTextBox control as follows:
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Controls;

namespace Sheva.Windows.Controls
{
public class InfoTextBox : TextBox
{
public InfoTextBox() : base() { }

static InfoTextBox()
{
TextProperty.OverrideMetadata(typeof(InfoTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(TextPropertyChanged)));
}

public static readonly DependencyProperty InfoTextProperty = DependencyProperty.Register(
"InfoText",
typeof(String),
typeof(InfoTextBox),
new PropertyMetadata(String.Empty));

public String InfoText
{
get { return (String)GetValue(InfoTextProperty); }
set { SetValue(InfoTextProperty, value); }
}

private static readonly DependencyPropertyKey HasTextPropertyKey = DependencyProperty.RegisterReadOnly(
"HasText",
typeof(Boolean),
typeof(InfoTextBox),
new FrameworkPropertyMetadata(false));

public static readonly DependencyProperty HasTextProperty = HasTextPropertyKey.DependencyProperty;

public Boolean HasText
{
get { return (Boolean)GetValue(HasTextProperty); }
}

private static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
InfoTextBox itb = (InfoTextBox)sender;

Boolean hasNoText = String.Equals(itb.Text, itb.InfoText) || String.Equals(itb.Text, String.Empty);
if (!hasNoText != itb.HasText)
{
itb.SetValue(HasTextPropertyKey, !hasNoText);
}
}

protected override void OnInitialized(EventArgs e)
{
FontStyle = FontStyles.Italic;
Foreground = Brushes.Gray;
SetValue(TextProperty, GetValue(InfoTextProperty));
base.OnInitialized(e);
}

protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
if (!HasText)
{
FontStyle = FontStyles.Normal;
Foreground = Brushes.Black;
SetValue(TextProperty, String.Empty);
}
base.OnGotKeyboardFocus(e);
}

protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
if (!HasText)
{
FontStyle = FontStyles.Italic;
Foreground = Brushes.Gray;
SetValue(TextProperty, GetValue(InfoTextProperty));
}
else
{
FontStyle = FontStyles.Normal;
Foreground = Brushes.Black;
}
base.OnLostKeyboardFocus(e);
}
}
}

You can use the custom control in XAML as follows:
<cc:InfoTextBox InfoText="Start Search" />
InfoText is where you can put your "gray background text" in:)

Edit: you should mark Drew Marsh's reply as the best answer.

Sheva


 
 
Art_Sherwood





PostPosted: Windows Presentation Foundation (WPF), How do I implement the light gray background text in a TextBox? Top

This is what I ended up with...

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace MSCAF
{
public class InfoTextBox : TextBox
{
public InfoTextBox () : base ()
{
}

static InfoTextBox ()
{
DefaultStyleKeyProperty.OverrideMetadata (typeof (InfoTextBox), new FrameworkPropertyMetadata (typeof (InfoTextBox)));
TextProperty.OverrideMetadata (typeof (InfoTextBox), new FrameworkPropertyMetadata (new PropertyChangedCallback (TextPropertyChanged)));
}

public static readonly DependencyProperty InfoProperty = DependencyProperty.Register (
"Info",
typeof (String),
typeof (InfoTextBox),
new PropertyMetadata (String.Empty)
);

public string Info
{
get { return (String)GetValue(InfoProperty); }
set { SetValue(InfoProperty, value); }
}

private static readonly DependencyPropertyKey HasTextPropertyKey = DependencyProperty.RegisterReadOnly(
"HasText",
typeof(Boolean),
typeof(InfoTextBox),
new FrameworkPropertyMetadata(false)
);

public static readonly DependencyProperty HasTextProperty = HasTextPropertyKey.DependencyProperty;

public Boolean HasText
{
get { return (Boolean)GetValue(HasTextProperty); }
}

static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
InfoTextBox itb = (InfoTextBox)sender;

Boolean actualHasText = itb.Text.Length > 0;
if (actualHasText != itb.HasText)
{
itb.SetValue(HasTextPropertyKey, actualHasText);
}
}
}
}



Then, in a resource dictionary I have the following...

<Style x:Key="{x:Type local:InfoTextBox}" TargetType="{x:Type local:InfoTextBox}">
<Style.BasedOn>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="#FF7F9DB9"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="BorderThickness" Value="1,1,1,1"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Padding" Value="4,5,4,5"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:InfoTextBox}">
<Grid Margin="0">
<Border Background="{TemplateBinding Background}" x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
<Label
x:Name="Message"
Foreground="Gray"
Opacity="0.9"
FontSize="{TemplateBinding FontSize}"
FontStyle="Italic"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="0"
Content="{TemplateBinding Info}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" TargetName="Bd"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="Width" Value="Auto">
<Setter Property="MinWidth" Value="100"/>
</Trigger>
<Trigger Property="Height" Value="Auto">
<Setter Property="MinHeight" Value="20"/>
</Trigger>
<Trigger Property="HasText" Value="True">
<Setter TargetName="Message" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.BasedOn>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>

This seems to be working well for me.

I prefer this solution over Drew's because whether the info text shows is independent of keyboard focus.  It is only dependent on whether there is user-entered text in the control.  I thought your solution came much closer to what I was looking for, I just removed the border stuff that I wasn't interested in and made it descend from TextBox which you seem to have done as well.  I like the idea of having the info text not be the actual text in the edit box but a visual layer on top of the text box.  Was there some other reason why you felt your solution was inferior to Drew's in this situation