c# - poner - Marca de agua/texto de sugerencia/marcador de posición TextBox
texto de fondo en textbox c# wpf (30)
Solución simple usando estilo:
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Style.Resources>
<VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<Label Content="MM:SS:HH AM/PM" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Gran solución:
https://code.msdn.microsoft.com/windowsdesktop/In-place-hit-messages-for-18db3a6c
¿Cómo puedo poner texto en un cuadro de texto que se elimina automáticamente cuando el usuario escribe algo en él? (En WPF)
@Veton: Me gusta mucho la simplicidad de su solución, pero mi reputación no es lo suficientemente alta como para que lo golpee todavía.
@Tim Murphy: el error de "Enlace bidireccional requiere ruta o XPath" fue una solución fácil ... código actualizado que incluye algunos otros pequeños ajustes (solo WPF probado):
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
public class TextBoxWatermarked : TextBox
{
public string Watermark
{
get { return (string)GetValue(WaterMarkProperty); }
set { SetValue(WaterMarkProperty, value); }
}
public static readonly DependencyProperty WaterMarkProperty =
DependencyProperty.Register("Watermark", typeof(string), typeof(TextBoxWatermarked), new PropertyMetadata(new PropertyChangedCallback(OnWatermarkChanged)));
private bool _isWatermarked = false;
private Binding _textBinding = null;
public TextBoxWatermarked()
{
Loaded += (s, ea) => ShowWatermark();
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
HideWatermark();
}
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
ShowWatermark();
}
private static void OnWatermarkChanged(DependencyObject sender, DependencyPropertyChangedEventArgs ea)
{
var tbw = sender as TextBoxWatermarked;
if (tbw == null || !tbw.IsLoaded) return; //needed to check IsLoaded so that we didn''t dive into the ShowWatermark() routine before initial Bindings had been made
tbw.ShowWatermark();
}
private void ShowWatermark()
{
if (String.IsNullOrEmpty(Text) && !String.IsNullOrEmpty(Watermark))
{
_isWatermarked = true;
//save the existing binding so it can be restored
_textBinding = BindingOperations.GetBinding(this, TextProperty);
//blank out the existing binding so we can throw in our Watermark
BindingOperations.ClearBinding(this, TextProperty);
//set the signature watermark gray
Foreground = new SolidColorBrush(Colors.Gray);
//display our watermark text
Text = Watermark;
}
}
private void HideWatermark()
{
if (_isWatermarked)
{
_isWatermarked = false;
ClearValue(ForegroundProperty);
Text = "";
if (_textBinding != null) SetBinding(TextProperty, _textBinding);
}
}
}
David Owens tiene un ejemplo más complicado de un "Cuadro de búsqueda" completo here .
Hay un artículo en CodeProject sobre cómo hacerlo en "3 líneas de XAML".
<Grid Background="{StaticResource brushWatermarkBackground}">
<TextBlock Margin="5,2" Text="Type something..."
Foreground="{StaticResource brushForeground}"
Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty,
Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox Name="txtUserEntry" Background="Transparent"
BorderBrush="{StaticResource brushBorder}" />
</Grid>
Ok, bueno, podría no tener 3 líneas de formato XAML, pero es bastante simple.
Una cosa a tener en cuenta, sin embargo, es que utiliza un método de extensión no estándar en la propiedad Text, llamada "IsEmpty". Debe implementar esto usted mismo, sin embargo, el artículo no parece mencionar eso.
He creado una implementación única de código siple que funciona bien para WPF y Silverlight también:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
public class TextBoxWatermarked : TextBox
{
#region [ Dependency Properties ]
public static DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark",
typeof(string),
typeof(TextBoxWatermarked),
new PropertyMetadata(new PropertyChangedCallback(OnWatermarkChanged)));
#endregion
#region [ Fields ]
private bool _isWatermarked;
private Binding _textBinding;
#endregion
#region [ Properties ]
protected new Brush Foreground
{
get { return base.Foreground; }
set { base.Foreground = value; }
}
public string Watermark
{
get { return (string)GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
#endregion
#region [ .ctor ]
public TextBoxWatermarked()
{
Loaded += (s, ea) => ShowWatermark();
}
#endregion
#region [ Event Handlers ]
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
HideWatermark();
}
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
ShowWatermark();
}
private static void OnWatermarkChanged(DependencyObject sender, DependencyPropertyChangedEventArgs ea)
{
var tbw = sender as TextBoxWatermarked;
if (tbw == null) return;
tbw.ShowWatermark();
}
#endregion
#region [ Methods ]
private void ShowWatermark()
{
if (string.IsNullOrEmpty(base.Text))
{
_isWatermarked = true;
base.Foreground = new SolidColorBrush(Colors.Gray);
var bindingExpression = GetBindingExpression(TextProperty);
_textBinding = bindingExpression == null ? null : bindingExpression.ParentBinding;
if (bindingExpression != null)
bindingExpression.UpdateSource();
SetBinding(TextProperty, new Binding());
base.Text = Watermark;
}
}
private void HideWatermark()
{
if (_isWatermarked)
{
_isWatermarked = false;
ClearValue(ForegroundProperty);
base.Text = "";
SetBinding(TextProperty, _textBinding ?? new Binding());
}
}
#endregion
}
Uso:
<TextBoxWatermarked Watermark="Some text" />
Me encontré con un poco de dificultad al usar el code @ john-myczek con un TextBox enlazado. Como el TextBox no genera un evento de enfoque cuando se actualiza, la marca de agua permanecerá visible debajo del nuevo texto. Para solucionar esto, simplemente agregué otro controlador de eventos:
if (d is ComboBox || d is TextBox)
{
control.GotKeyboardFocus += Control_GotKeyboardFocus;
control.LostKeyboardFocus += Control_Loaded;
if (d is TextBox)
(d as TextBox).TextChanged += Control_TextChanged;
}
private static void Control_TextChanged(object sender, RoutedEventArgs e)
{
var tb = (TextBox)sender;
if (ShouldShowWatermark(tb))
{
ShowWatermark(tb);
}
else
{
RemoveWatermark(tb);
}
}
No puedo creer que nadie haya publicado el obvio kit de herramientas de WPF extendido: WatermarkTextBox de Xceed. Funciona bastante bien y es de código abierto en caso de que quiera personalizarlo.
Puede crear una marca de agua que se puede agregar a cualquier TextBox
con una propiedad adjunta. Aquí está la fuente de la propiedad adjunta:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
/// <summary>
/// Class that provides the Watermark attached property
/// </summary>
public static class WatermarkService
{
/// <summary>
/// Watermark Attached Dependency Property
/// </summary>
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
"Watermark",
typeof(object),
typeof(WatermarkService),
new FrameworkPropertyMetadata((object)null, new PropertyChangedCallback(OnWatermarkChanged)));
#region Private Fields
/// <summary>
/// Dictionary of ItemsControls
/// </summary>
private static readonly Dictionary<object, ItemsControl> itemsControls = new Dictionary<object, ItemsControl>();
#endregion
/// <summary>
/// Gets the Watermark property. This dependency property indicates the watermark for the control.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> to get the property from</param>
/// <returns>The value of the Watermark property</returns>
public static object GetWatermark(DependencyObject d)
{
return (object)d.GetValue(WatermarkProperty);
}
/// <summary>
/// Sets the Watermark property. This dependency property indicates the watermark for the control.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> to set the property on</param>
/// <param name="value">value of the property</param>
public static void SetWatermark(DependencyObject d, object value)
{
d.SetValue(WatermarkProperty, value);
}
/// <summary>
/// Handles changes to the Watermark property.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> that fired the event</param>
/// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Control control = (Control)d;
control.Loaded += Control_Loaded;
if (d is ComboBox)
{
control.GotKeyboardFocus += Control_GotKeyboardFocus;
control.LostKeyboardFocus += Control_Loaded;
}
else if (d is TextBox)
{
control.GotKeyboardFocus += Control_GotKeyboardFocus;
control.LostKeyboardFocus += Control_Loaded;
((TextBox)control).TextChanged += Control_GotKeyboardFocus;
}
if (d is ItemsControl && !(d is ComboBox))
{
ItemsControl i = (ItemsControl)d;
// for Items property
i.ItemContainerGenerator.ItemsChanged += ItemsChanged;
itemsControls.Add(i.ItemContainerGenerator, i);
// for ItemsSource property
DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, i.GetType());
prop.AddValueChanged(i, ItemsSourceChanged);
}
}
#region Event Handlers
/// <summary>
/// Handle the GotFocus event on the control
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
private static void Control_GotKeyboardFocus(object sender, RoutedEventArgs e)
{
Control c = (Control)sender;
if (ShouldShowWatermark(c))
{
ShowWatermark(c);
}
else
{
RemoveWatermark(c);
}
}
/// <summary>
/// Handle the Loaded and LostFocus event on the control
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
private static void Control_Loaded(object sender, RoutedEventArgs e)
{
Control control = (Control)sender;
if (ShouldShowWatermark(control))
{
ShowWatermark(control);
}
}
/// <summary>
/// Event handler for the items source changed event
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
private static void ItemsSourceChanged(object sender, EventArgs e)
{
ItemsControl c = (ItemsControl)sender;
if (c.ItemsSource != null)
{
if (ShouldShowWatermark(c))
{
ShowWatermark(c);
}
else
{
RemoveWatermark(c);
}
}
else
{
ShowWatermark(c);
}
}
/// <summary>
/// Event handler for the items changed event
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
private static void ItemsChanged(object sender, ItemsChangedEventArgs e)
{
ItemsControl control;
if (itemsControls.TryGetValue(sender, out control))
{
if (ShouldShowWatermark(control))
{
ShowWatermark(control);
}
else
{
RemoveWatermark(control);
}
}
}
#endregion
#region Helper Methods
/// <summary>
/// Remove the watermark from the specified element
/// </summary>
/// <param name="control">Element to remove the watermark from</param>
private static void RemoveWatermark(UIElement control)
{
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);
// layer could be null if control is no longer in the visual tree
if (layer != null)
{
Adorner[] adorners = layer.GetAdorners(control);
if (adorners == null)
{
return;
}
foreach (Adorner adorner in adorners)
{
if (adorner is WatermarkAdorner)
{
adorner.Visibility = Visibility.Hidden;
layer.Remove(adorner);
}
}
}
}
/// <summary>
/// Show the watermark on the specified control
/// </summary>
/// <param name="control">Control to show the watermark on</param>
private static void ShowWatermark(Control control)
{
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);
// layer could be null if control is no longer in the visual tree
if (layer != null)
{
layer.Add(new WatermarkAdorner(control, GetWatermark(control)));
}
}
/// <summary>
/// Indicates whether or not the watermark should be shown on the specified control
/// </summary>
/// <param name="c"><see cref="Control"/> to test</param>
/// <returns>true if the watermark should be shown; false otherwise</returns>
private static bool ShouldShowWatermark(Control c)
{
if (c is ComboBox)
{
return (c as ComboBox).Text == string.Empty;
}
else if (c is TextBoxBase)
{
return (c as TextBox).Text == string.Empty;
}
else if (c is ItemsControl)
{
return (c as ItemsControl).Items.Count == 0;
}
else
{
return false;
}
}
#endregion
}
La propiedad adjunta utiliza una clase llamada WatermarkAdorner
, aquí está esa fuente:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
/// <summary>
/// Adorner for the watermark
/// </summary>
internal class WatermarkAdorner : Adorner
{
#region Private Fields
/// <summary>
/// <see cref="ContentPresenter"/> that holds the watermark
/// </summary>
private readonly ContentPresenter contentPresenter;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="WatermarkAdorner"/> class
/// </summary>
/// <param name="adornedElement"><see cref="UIElement"/> to be adorned</param>
/// <param name="watermark">The watermark</param>
public WatermarkAdorner(UIElement adornedElement, object watermark) :
base(adornedElement)
{
this.IsHitTestVisible = false;
this.contentPresenter = new ContentPresenter();
this.contentPresenter.Content = watermark;
this.contentPresenter.Opacity = 0.5;
this.contentPresenter.Margin = new Thickness(Control.Margin.Left + Control.Padding.Left, Control.Margin.Top + Control.Padding.Top, 0, 0);
if (this.Control is ItemsControl && !(this.Control is ComboBox))
{
this.contentPresenter.VerticalAlignment = VerticalAlignment.Center;
this.contentPresenter.HorizontalAlignment = HorizontalAlignment.Center;
}
// Hide the control adorner when the adorned element is hidden
Binding binding = new Binding("IsVisible");
binding.Source = adornedElement;
binding.Converter = new BooleanToVisibilityConverter();
this.SetBinding(VisibilityProperty, binding);
}
#endregion
#region Protected Properties
/// <summary>
/// Gets the number of children for the <see cref="ContainerVisual"/>.
/// </summary>
protected override int VisualChildrenCount
{
get { return 1; }
}
#endregion
#region Private Properties
/// <summary>
/// Gets the control that is being adorned
/// </summary>
private Control Control
{
get { return (Control)this.AdornedElement; }
}
#endregion
#region Protected Overrides
/// <summary>
/// Returns a specified child <see cref="Visual"/> for the parent <see cref="ContainerVisual"/>.
/// </summary>
/// <param name="index">A 32-bit signed integer that represents the index value of the child <see cref="Visual"/>. The value of index must be between 0 and <see cref="VisualChildrenCount"/> - 1.</param>
/// <returns>The child <see cref="Visual"/>.</returns>
protected override Visual GetVisualChild(int index)
{
return this.contentPresenter;
}
/// <summary>
/// Implements any custom measuring behavior for the adorner.
/// </summary>
/// <param name="constraint">A size to constrain the adorner to.</param>
/// <returns>A <see cref="Size"/> object representing the amount of layout space needed by the adorner.</returns>
protected override Size MeasureOverride(Size constraint)
{
// Here''s the secret to getting the adorner to cover the whole control
this.contentPresenter.Measure(Control.RenderSize);
return Control.RenderSize;
}
/// <summary>
/// When overridden in a derived class, positions child elements and determines a size for a <see cref="FrameworkElement"/> derived class.
/// </summary>
/// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
/// <returns>The actual size used.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
this.contentPresenter.Arrange(new Rect(finalSize));
return finalSize;
}
#endregion
}
Ahora puedes poner una marca de agua en cualquier TextBox como este:
<AdornerDecorator>
<TextBox x:Name="SearchTextBox">
<controls:WatermarkService.Watermark>
<TextBlock>Type here to search text</TextBlock>
</controls:WatermarkService.Watermark>
</TextBox>
</AdornerDecorator>
La marca de agua puede ser lo que quieras (texto, imágenes ...). Además de trabajar para TextBoxes, esta marca de agua también funciona para ComboBoxes y ItemControls.
Este código fue adaptado de esta publicación de blog .
Si está escribiendo aplicaciones UWP en Windows 10, esto es mucho más fácil.
Más información: https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.textbox.placeholdertext.aspx
Solo usando XAML, sin extensiones, sin convertidores:
<Grid>
<TextBox Width="250" VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="SearchTermTextBox" Margin="5"/>
<TextBlock IsHitTestVisible="False" Text="Enter Search Term Here" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0" Foreground="DarkGray">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=SearchTermTextBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
Vi la solución de John Myczek y sus comentarios sobre Compatibilidad con ComboBox
y PasswordBox
, así que mejoré la solución de John Myczek y aquí está:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
/// <summary>
/// Class that provides the Watermark attached property
/// </summary>
public static class WatermarkService
{
/// <summary>
/// Watermark Attached Dependency Property
/// </summary>
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
"Watermark",
typeof(object),
typeof(WatermarkService),
new FrameworkPropertyMetadata((object)null, new PropertyChangedCallback(OnWatermarkChanged)));
#region Private Fields
/// <summary>
/// Dictionary of ItemsControls
/// </summary>
private static readonly Dictionary<object, ItemsControl> itemsControls = new Dictionary<object, ItemsControl>();
#endregion
/// <summary>
/// Gets the Watermark property. This dependency property indicates the watermark for the control.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> to get the property from</param>
/// <returns>The value of the Watermark property</returns>
public static object GetWatermark(DependencyObject d)
{
return (object)d.GetValue(WatermarkProperty);
}
/// <summary>
/// Sets the Watermark property. This dependency property indicates the watermark for the control.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> to set the property on</param>
/// <param name="value">value of the property</param>
public static void SetWatermark(DependencyObject d, object value)
{
d.SetValue(WatermarkProperty, value);
}
/// <summary>
/// Handles changes to the Watermark property.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> that fired the event</param>
/// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Control control = (Control)d;
control.Loaded += Control_Loaded;
if (d is TextBox || d is PasswordBox)
{
control.GotKeyboardFocus += Control_GotKeyboardFocus;
control.LostKeyboardFocus += Control_Loaded;
}
else if (d is ComboBox)
{
control.GotKeyboardFocus += Control_GotKeyboardFocus;
control.LostKeyboardFocus += Control_Loaded;
(d as ComboBox).SelectionChanged += new SelectionChangedEventHandler(SelectionChanged);
}
else if (d is ItemsControl)
{
ItemsControl i = (ItemsControl)d;
// for Items property
i.ItemContainerGenerator.ItemsChanged += ItemsChanged;
itemsControls.Add(i.ItemContainerGenerator, i);
// for ItemsSource property
DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, i.GetType());
prop.AddValueChanged(i, ItemsSourceChanged);
}
}
/// <summary>
/// Event handler for the selection changed event
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
private static void SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Control control = (Control)sender;
if (ShouldShowWatermark(control))
{
ShowWatermark(control);
}
else
{
RemoveWatermark(control);
}
}
#region Event Handlers
/// <summary>
/// Handle the GotFocus event on the control
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
private static void Control_GotKeyboardFocus(object sender, RoutedEventArgs e)
{
Control c = (Control)sender;
if (ShouldShowWatermark(c))
{
RemoveWatermark(c);
}
}
/// <summary>
/// Handle the Loaded and LostFocus event on the control
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
private static void Control_Loaded(object sender, RoutedEventArgs e)
{
Control control = (Control)sender;
if (ShouldShowWatermark(control))
{
ShowWatermark(control);
}
}
/// <summary>
/// Event handler for the items source changed event
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
private static void ItemsSourceChanged(object sender, EventArgs e)
{
ItemsControl c = (ItemsControl)sender;
if (c.ItemsSource != null)
{
if (ShouldShowWatermark(c))
{
ShowWatermark(c);
}
else
{
RemoveWatermark(c);
}
}
else
{
ShowWatermark(c);
}
}
/// <summary>
/// Event handler for the items changed event
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
private static void ItemsChanged(object sender, ItemsChangedEventArgs e)
{
ItemsControl control;
if (itemsControls.TryGetValue(sender, out control))
{
if (ShouldShowWatermark(control))
{
ShowWatermark(control);
}
else
{
RemoveWatermark(control);
}
}
}
#endregion
#region Helper Methods
/// <summary>
/// Remove the watermark from the specified element
/// </summary>
/// <param name="control">Element to remove the watermark from</param>
private static void RemoveWatermark(UIElement control)
{
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);
// layer could be null if control is no longer in the visual tree
if (layer != null)
{
Adorner[] adorners = layer.GetAdorners(control);
if (adorners == null)
{
return;
}
foreach (Adorner adorner in adorners)
{
if (adorner is WatermarkAdorner)
{
adorner.Visibility = Visibility.Hidden;
layer.Remove(adorner);
}
}
}
}
/// <summary>
/// Show the watermark on the specified control
/// </summary>
/// <param name="control">Control to show the watermark on</param>
private static void ShowWatermark(Control control)
{
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);
// layer could be null if control is no longer in the visual tree
if (layer != null)
{
layer.Add(new WatermarkAdorner(control, GetWatermark(control)));
}
}
/// <summary>
/// Indicates whether or not the watermark should be shown on the specified control
/// </summary>
/// <param name="c"><see cref="Control"/> to test</param>
/// <returns>true if the watermark should be shown; false otherwise</returns>
private static bool ShouldShowWatermark(Control c)
{
if (c is ComboBox)
{
return (c as ComboBox).SelectedItem == null;
//return (c as ComboBox).Text == string.Empty;
}
else if (c is TextBoxBase)
{
return (c as TextBox).Text == string.Empty;
}
else if (c is PasswordBox)
{
return (c as PasswordBox).Password == string.Empty;
}
else if (c is ItemsControl)
{
return (c as ItemsControl).Items.Count == 0;
}
else
{
return false;
}
}
#endregion
}
Ahora, un ComboBox
también puede ser Editable
, y PasswordBox
puede agregar una marca de agua. No olvide utilizar el comentario de JoanComasFdz anterior para resolver el problema del margen.
Y, por supuesto, todo el crédito es para John Myczek.
MahApps.Metro para WPF tiene un control de marca de agua incorporado, si prefieres no rodar el tuyo. Es bastante sencillo de usar.
<AdornerDecorator>
<TextBox Name="txtSomeText"
Width="200"
HorizontalAlignment="Right">
<Controls:TextBoxHelper.Watermark>I''m a watermark!</Controls:TextBoxHelper.Watermark>
</TextBox>
</AdornerDecorator>
Esta biblioteca tiene una marca de agua.
Uso de la muestra:
<TextBox adorners:Watermark.Text="Write something here"
adorners:Watermark.TextStyle="{StaticResource AdornerTextStyle}"
adorners:Watermark.VisibleWhen="EmptyAndNotKeyboardFocused"/>
Aquí está la solución más simple:
<Grid>
<Label Content="Placeholder text" VerticalAlignment="Center" Margin="10">
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Expression}" Value="">
<Setter Property="Foreground" Value="Gray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<TextBox HorizontalAlignment="Stretch" Margin="5" Background="Transparent"
Text="{Binding Expression, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Padding="5">
</TextBox>
</Grid>
Este es un cuadro de texto con fondo transparente que cubre una etiqueta. El texto gris de la etiqueta se vuelve transparente mediante un activador de datos que se activa siempre que el texto enlazado no sea una cadena vacía.
Bueno, aquí está el mío: no necesariamente el mejor, pero como es simple, es fácil de editar a tu gusto.
<UserControl x:Class="WPFControls.ShadowedTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFControls"
Name="Root">
<UserControl.Resources>
<local:ShadowConverter x:Key="ShadowConvert"/>
</UserControl.Resources>
<Grid>
<TextBox Name="textBox"
Foreground="{Binding ElementName=Root, Path=Foreground}"
Text="{Binding ElementName=Root, Path=Text, UpdateSourceTrigger=PropertyChanged}"
TextChanged="textBox_TextChanged"
TextWrapping="Wrap"
VerticalContentAlignment="Center"/>
<TextBlock Name="WaterMarkLabel"
IsHitTestVisible="False"
Foreground="{Binding ElementName=Root,Path=Foreground}"
FontWeight="Thin"
Opacity=".345"
FontStyle="Italic"
Text="{Binding ElementName=Root, Path=Watermark}"
VerticalAlignment="Center"
TextWrapping="Wrap"
TextAlignment="Center">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource ShadowConvert}">
<Binding ElementName="textBox" Path="Text"/>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
</Grid>
El convertidor, como está escrito ahora, no es necesario que sea un multiconvertidor, pero en este tiempo se puede extender fácilmente.
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WPFControls
{
class ShadowConverter:IMultiValueConverter
{
#region Implementation of IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var text = (string) values[0];
return text == string.Empty
? Visibility.Visible
: Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new object[0];
}
#endregion
}
}
y finalmente el código detrás:
using System.Windows;
using System.Windows.Controls;
namespace WPFControls
{
/// <summary>
/// Interaction logic for ShadowedTextBox.xaml
/// </summary>
public partial class ShadowedTextBox : UserControl
{
public event TextChangedEventHandler TextChanged;
public ShadowedTextBox()
{
InitializeComponent();
}
public static readonly DependencyProperty WatermarkProperty =
DependencyProperty.Register("Watermark",
typeof (string),
typeof (ShadowedTextBox),
new UIPropertyMetadata(string.Empty));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text",
typeof (string),
typeof (ShadowedTextBox),
new UIPropertyMetadata(string.Empty));
public static readonly DependencyProperty TextChangedProperty =
DependencyProperty.Register("TextChanged",
typeof (TextChangedEventHandler),
typeof (ShadowedTextBox),
new UIPropertyMetadata(null));
public string Watermark
{
get { return (string)GetValue(WatermarkProperty); }
set
{
SetValue(WatermarkProperty, value);
}
}
public string Text
{
get { return (string) GetValue(TextProperty); }
set{SetValue(TextProperty,value);}
}
private void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (TextChanged != null) TextChanged(this, e);
}
public void Clear()
{
textBox.Clear();
}
}
}
Configure el cuadro de texto con el texto del marcador de posición en un color suave ...
public MainWindow ( )
{
InitializeComponent ( );
txtInput.Text = "Type something here...";
txtInput.Foreground = Brushes.DimGray;
}
Cuando el cuadro de texto se enfoca, elimínelo y cambie el color del texto
private void txtInput_GotFocus ( object sender, EventArgs e )
{
MessageBox.Show ( "got focus" );
txtInput.Text = "";
txtInput.Foreground = Brushes.Red;
}
Encontré esta manera de hacerlo de una manera muy rápida y fácil.
<ComboBox x:Name="comboBox1" SelectedIndex="0" HorizontalAlignment="Left" Margin="202,43,0,0" VerticalAlignment="Top" Width="149">
<ComboBoxItem Visibility="Collapsed">
<TextBlock Foreground="Gray" FontStyle="Italic">Please select ...</TextBlock>
</ComboBoxItem>
<ComboBoxItem Name="cbiFirst1">First Item</ComboBoxItem>
<ComboBoxItem Name="cbiSecond1">Second Item</ComboBoxItem>
<ComboBoxItem Name="cbiThird1">third Item</ComboBoxItem>
</ComboBox>
Tal vez pueda ayudar a cualquiera que intente hacer esto.
Fuente: http://www.admindiaries.com/displaying-a-please-select-watermark-type-text-in-a-wpf-combobox/
Este es mi enfoque. Es ideal para MVVM, donde también verifico si el cuadro de texto tiene el foco, también puede usar un disparador regular solo para el valor del texto, y el punto es que solo cambio la imagen de fondo cuando el valor cambia:
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="True"/>
<Condition Property="Text" Value=""/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/Images/Scan.PNG" Stretch="Uniform" AlignmentX="Left"/>
</Setter.Value>
</Setter>
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Este es un ejemplo que muestra cómo crear un cuadro de texto de marca de agua en WPF:
<Window x:Class="WaterMarkTextBoxDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WaterMarkTextBoxDemo"
Height="200" Width="400">
<Window.Resources>
<SolidColorBrush x:Key="brushWatermarkBackground" Color="White" />
<SolidColorBrush x:Key="brushWatermarkForeground" Color="LightSteelBlue" />
<SolidColorBrush x:Key="brushWatermarkBorder" Color="Indigo" />
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<local:TextInputToVisibilityConverter x:Key="TextInputToVisibilityConverter" />
<Style x:Key="EntryFieldStyle" TargetType="Grid" >
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="20,0" />
</Style>
</Window.Resources>
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
<TextBlock Margin="5,2" Text="This prompt dissappears as you type..." Foreground="{StaticResource brushWatermarkForeground}"
Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox Name="txtUserEntry" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
</Grid>
<Grid Grid.Row="1" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
<TextBlock Margin="5,2" Text="This dissappears as the control gets focus..." Foreground="{StaticResource brushWatermarkForeground}" >
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource TextInputToVisibilityConverter}">
<Binding ElementName="txtUserEntry2" Path="Text.IsEmpty" />
<Binding ElementName="txtUserEntry2" Path="IsFocused" />
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
<TextBox Name="txtUserEntry2" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
</Grid>
</Grid>
</Window>
TextInputToVisibilityConverter se define como:
using System;
using System.Windows.Data;
using System.Windows;
namespace WaterMarkTextBoxDemo
{
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();
}
}
}
Nota: Este no es mi código. Lo encontré here , pero creo que este es el mejor enfoque.
Mi solución es bastante simple.
En mi ventana de inicio de sesión. El xaml es así.
<DockPanel HorizontalAlignment="Center" VerticalAlignment="Center" Height="80" Width="300" LastChildFill="True">
<Button Margin="5,0,0,0" Click="login_Click" DockPanel.Dock="Right" VerticalAlignment="Center" ToolTip="Login to system">
Login
</Button>
<StackPanel>
<TextBox x:Name="userNameWatermarked" Height="25" Foreground="Gray" Text="UserName" GotFocus="userNameWatermarked_GotFocus"></TextBox>
<TextBox x:Name="userName" Height="25" TextChanged="loginElement_TextChanged" Visibility="Collapsed" LostFocus="userName_LostFocus" ></TextBox>
<TextBox x:Name="passwordWatermarked" Height="25" Foreground="Gray" Text="Password" Margin="0,5,0,5" GotFocus="passwordWatermarked_GotFocus"></TextBox>
<PasswordBox x:Name="password" Height="25" PasswordChanged="password_PasswordChanged" KeyUp="password_KeyUp" LostFocus="password_LostFocus" Margin="0,5,0,5" Visibility="Collapsed"></PasswordBox>
<TextBlock x:Name="loginError" Visibility="Hidden" Foreground="Red" FontSize="12"></TextBlock>
</StackPanel>
</DockPanel>
El código es así.
private void userNameWatermarked_GotFocus(object sender, RoutedEventArgs e)
{
userNameWatermarked.Visibility = System.Windows.Visibility.Collapsed;
userName.Visibility = System.Windows.Visibility.Visible;
userName.Focus();
}
private void userName_LostFocus(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(this.userName.Text))
{
userName.Visibility = System.Windows.Visibility.Collapsed;
userNameWatermarked.Visibility = System.Windows.Visibility.Visible;
}
}
private void passwordWatermarked_GotFocus(object sender, RoutedEventArgs e)
{
passwordWatermarked.Visibility = System.Windows.Visibility.Collapsed;
password.Visibility = System.Windows.Visibility.Visible;
password.Focus();
}
private void password_LostFocus(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(this.password.Password))
{
password.Visibility = System.Windows.Visibility.Collapsed;
passwordWatermarked.Visibility = System.Windows.Visibility.Visible;
}
}
Solo decide ocultar o mostrar el cuadro de texto de marca de agua es suficiente. Aunque no es hermoso, pero funciona bien.
Decidí resolver esto a través de un Comportamiento. Utiliza una Hint
propiedad para definir el texto que se muestra (también podría ser un objeto, si lo prefiere) y una Value
propiedad para evaluar si la sugerencia debe ser visible o no.
El comportamiento se declara de la siguiente manera:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;
using System.Windows.Media;
public class HintBehavior : Behavior<ContentControl>
{
public static readonly DependencyProperty HintProperty = DependencyProperty
.Register("Hint", typeof (string), typeof (HintBehavior)
//, new FrameworkPropertyMetadata(null, OnHintChanged)
);
public string Hint
{
get { return (string) GetValue(HintProperty); }
set { SetValue(HintProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty
.Register("Value", typeof (object), typeof (HintBehavior)
, new FrameworkPropertyMetadata(null, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var visible = e.NewValue == null;
d.SetValue(VisibilityProperty, visible ? Visibility.Visible : Visibility.Collapsed);
}
public object Value
{
get { return GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty VisibilityProperty = DependencyProperty
.Register("Visibility", typeof (Visibility), typeof (HintBehavior)
, new FrameworkPropertyMetadata(Visibility.Visible
//, new PropertyChangedCallback(OnVisibilityChanged)
));
public Visibility Visibility
{
get { return (Visibility) GetValue(VisibilityProperty); }
set { SetValue(VisibilityProperty, value); }
}
public static readonly DependencyProperty ForegroundProperty = DependencyProperty
.Register("Foreground", typeof (Brush), typeof (HintBehavior)
, new FrameworkPropertyMetadata(new SolidColorBrush(Colors.DarkGray)
//, new PropertyChangedCallback(OnForegroundChanged)
));
public Brush Foreground
{
get { return (Brush) GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
public static readonly DependencyProperty MarginProperty = DependencyProperty
.Register("Margin", typeof (Thickness), typeof (HintBehavior)
, new FrameworkPropertyMetadata(new Thickness(4, 5, 0, 0)
//, new PropertyChangedCallback(OnMarginChanged)
));
public Thickness Margin
{
get { return (Thickness) GetValue(MarginProperty); }
set { SetValue(MarginProperty, value); }
}
private static ResourceDictionary _hintBehaviorResources;
public static ResourceDictionary HintBehaviorResources
{
get
{
if (_hintBehaviorResources == null)
{
var res = new ResourceDictionary
{
Source = new Uri("/Mayflower.Client.Core;component/Behaviors/HintBehaviorResources.xaml",
UriKind.RelativeOrAbsolute)
};
_hintBehaviorResources = res;
}
return _hintBehaviorResources;
}
}
protected override void OnAttached()
{
base.OnAttached();
var t = (ControlTemplate) HintBehaviorResources["HintBehaviorWrapper"];
AssociatedObject.Template = t;
AssociatedObject.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
AssociatedObject.Loaded -= OnLoaded;
var label = (Label) AssociatedObject.Template.FindName("PART_HintLabel", AssociatedObject);
label.DataContext = this;
//label.Content = "Hello...";
label.SetBinding(UIElement.VisibilityProperty, new Binding("Visibility") {Source = this, Mode = BindingMode.OneWay});
label.SetBinding(ContentControl.ContentProperty, new Binding("Hint") {Source = this, Mode = BindingMode.OneWay});
label.SetBinding(Control.ForegroundProperty, new Binding("Foreground") {Source = this, Mode = BindingMode.OneWay});
label.SetBinding(FrameworkElement.MarginProperty, new Binding("Margin") {Source = this, Mode = BindingMode.OneWay});
}
}
Envuelve el objetivo con su propia plantilla, agregándole una etiqueta:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="HintBehaviorWrapper" TargetType="{x:Type ContentControl}">
<Grid>
<ContentPresenter Content="{TemplateBinding Content}" />
<Label x:Name="PART_HintLabel" IsHitTestVisible="False" Padding="0" />
</Grid>
</ControlTemplate>
</ResourceDictionary>
Para usarlo, solo agréguelo como un comportamiento y vincule sus valores (en mi caso lo agrego en una ControlTemplate, de ahí el enlace):
<ContentControl>
<i:Interaction.Behaviors>
<behaviors:HintBehavior Value="{Binding Property, RelativeSource={RelativeSource TemplatedParent}}"
Hint="{Binding Hint, RelativeSource={RelativeSource TemplatedParent}}" />
</i:Interaction.Behaviors>
<TextBox ... />
</ContentControl>
Me encantaría recibir comentarios si esto se considera una solución limpia. No requiere diccionarios estáticos y por lo tanto no tiene pérdida de memoria.
Esta técnica utiliza la propiedad Fondo para mostrar / ocultar el cuadro de texto de marcador de posición.
El marcador de posición se muestra evento cuando Textbox tiene el foco
Cómo funciona:
- Cuando está vacío, el fondo TextBox se establece en Transparente para mostrar el texto de PlaceHolder.
- Cuando no esté vacío, configure el fondo en blanco para cubrir el texto de PlaceHolder.
Aquí está el ejemplo básico. Para mis propios propósitos, convertí esto en un UserControl.
<Grid>
<Grid.Resources>
<ux:NotEmptyConverter x:Key="NotEmptyConverter" />
<Style TargetType="{x:Type Control}" x:Key="DefaultStyle">
<Setter Property="FontSize" Value="20" />
<Setter Property="Margin" Value="10"/>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="VerticalContentAlignment" Value="Center"></Setter>
</Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource DefaultStyle}"></Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="Placeholder Text Is Here" Foreground="DarkGray" />
<TextBox Grid.Row="0" Name="TextBoxEdit"
Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<TextBox.Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource DefaultStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=FirstName.Length, FallbackValue=0, TargetNullValue=0}" Value="0">
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=FirstName, FallbackValue=0, TargetNullValue=0, Converter={StaticResource NotEmptyConverter}}" Value="false">
<Setter Property="Background" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
Aquí está el ValueConverter para detectar cadenas no vacías en el DataTrigger.
public class NotEmptyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var s = value as string;
return string.IsNullOrEmpty(s);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Puede mantener un valor separado para el texto introducido y establecerlo junto con el campo "Texto" del cuadro de texto en los eventos "GotFocus" y "LostFocus". Cuando obtenga el enfoque, querrá borrar el cuadro de texto si no hay ningún valor. Y cuando pierda el enfoque, querrá establecer el valor de "Texto" del cuadro de texto y luego restablecer el valor de "Texto" del cuadro de texto en el marcador de posición si está vacío.
private String username = "";
private void usernameTextBox_GotFocus(object sender, RoutedEventArgs e) {
if (String.IsNullOrEmpty(username)) {
usernameTextBox.Text = "";
}
}
private void usernameTextBox_LostFocus(object sender, RoutedEventArgs e) {
username = usernameTextBox.Text;
if (String.IsNullOrEmpty(usernameTextBox.Text)) {
usernameTextBox.Text = "Username";
}
}
Luego, solo debe asegurarse de que el valor de "Texto" del cuadro de texto se inicialice con el texto del marcador de posición.
<TextBox x:Name="usernameTextBox" Text="Username" GotFocus="usernameTextBox_GotFocus" LostFocus="usernameTextBox_LostFocus" />
Además, puede extraer esto en una clase que amplíe la clase "TextBox" y luego reutilizarla en su proyecto.
namespace UI {
public class PlaceholderTextBox : TextBox {
public String Value { get; set; }
public String PlaceholderText { get; set; }
public Brush PlaceholderBrush { get; set; }
private Brush ValuedBrush { get; set; }
public PlaceholderTextBox() : base() {}
protected override void OnInitialized(EventArgs e) {
base.OnInitialized(e);
ValuedBrush = this.Foreground;
if (String.IsNullOrEmpty(this.Text)) {
this.Text = PlaceholderText;
this.Foreground = PlaceholderBrush;
}
}
protected override void OnGotFocus(System.Windows.RoutedEventArgs e) {
this.Foreground = ValuedBrush;
if (String.IsNullOrEmpty(Value)) {
this.Text = "";
}
base.OnGotFocus(e);
}
protected override void OnLostFocus(System.Windows.RoutedEventArgs e) {
Value = this.Text;
if (String.IsNullOrEmpty(this.Text)) {
this.Text = PlaceholderText;
this.Foreground = PlaceholderBrush;
}
base.OnLostFocus(e);
}
}
}
Y luego esto se puede agregar directamente en el xaml.
<Window x:Class="UI.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:UI"
Initialized="Window_Initialized">
<Grid>
<m:PlaceholderTextBox x:Name="usernameTextBox" PlaceholderText="Username" PlaceholderBrush="Gray" />
</Grid>
</Window>
Puedes usar GetFocus()
y LostFocus()
eventos para hacer esto.
Aquí está el ejemplo:
private void txtData1_GetFocus(object sender, RoutedEventArgs e)
{
if (txtData1.Text == "TextBox1abc")
{
txtData1.Text = string.Empty;
}
}
private void txtData1_LostFocus(object sender, RoutedEventArgs e)
{
if (txtData1.Text == string.Empty)
{
txtData1.Text = "TextBox1abc";
}
}
Si en lugar de tener la visibilidad de la marca de agua depende del estado de enfoque del control, desea que dependa de si el usuario ha ingresado algún texto, puede actualizar la respuesta de John Myczek (desde OnWatermarkChanged
abajo) a
static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var textbox = (TextBox)d;
textbox.Loaded += UpdateWatermark;
textbox.TextChanged += UpdateWatermark;
}
static void UpdateWatermark(object sender, RoutedEventArgs e) {
var textbox = (TextBox)sender;
var layer = AdornerLayer.GetAdornerLayer(textbox);
if (layer != null) {
if (textbox.Text == string.Empty) {
layer.Add(new WatermarkAdorner(textbox, GetWatermark(textbox)));
} else {
var adorners = layer.GetAdorners(textbox);
if (adorners == null) {
return;
}
foreach (var adorner in adorners) {
if (adorner is WatermarkAdorner) {
adorner.Visibility = Visibility.Hidden;
layer.Remove(adorner);
}
}
}
}
}
Esto tiene más sentido si su cuadro de texto se enfoca automáticamente al mostrar el formulario o al vincular el enlace de datos con la propiedad Texto.
Además, si su marca de agua siempre es solo una cadena, y necesita el estilo de la marca de agua para que coincida con el estilo del cuadro de texto, entonces en Adorner haga lo siguiente:
contentPresenter = new ContentPresenter {
Content = new TextBlock {
Text = (string)watermark,
Foreground = Control.Foreground,
Background = Control.Background,
FontFamily = Control.FontFamily,
FontSize = Control.FontSize,
...
},
...
}
hola pongo esta tarea en un comportamiento así que solo tienes que agregar algo como esto a tu cuadro de texto
<i:Interaction.Behaviors>
<Behaviors:TextBoxWatermarkBehavior Label="Test Watermark" LabelStyle="{StaticResource StyleWatermarkLabel}"/>
</i:Interaction.Behaviors>
puedes encontrar mi blog here
<TextBox Grid.Column="0" Name="TextBox_SearchBar" AcceptsReturn="False" AcceptsTab="False" TextWrapping="Wrap" KeyUp="TextBox_SearchBar_KeyUp" />
<TextBox Grid.Column="0" Name="TextBox_Watermark" Text="Search Test Sets" Foreground="Gray" GotFocus="TextBox_Watermark_GotFocus" />
private void TextBox_Watermark_GotFocus( object sender, RoutedEventArgs e ) {
TextBox_Watermark.Visibility = Visibility.Hidden;
TextBox_SearchBar.Focus();
}
<TextBox x:Name="OrderTxt" HorizontalAlignment="Left" VerticalAlignment="Top" VerticalContentAlignment="Center" Margin="10,10,0,0" Width="188" Height="32"/>
<Label IsHitTestVisible="False" Content="Order number" DataContext="{Binding ElementName=OrderTxt}" Foreground="DarkGray">
<Label.Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Width" Value="{Binding Width}"/>
<Setter Property="Height" Value="{Binding Height}"/>
<Setter Property="Margin" Value="{Binding Margin}"/>
<Setter Property="VerticalAlignment" Value="{Binding VerticalAlignment}"/>
<Setter Property="HorizontalAlignment" Value="{Binding HorizontalAlignment}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Window.Resources>
<Style x:Key="TextBoxUserStyle" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Width" Value="225"/>
<Setter Property="Height" Value="25"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="OuterBorder" BorderBrush="#5AFFFFFF" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
<Border x:Name="InnerBorder" Background="#FFFFFFFF" BorderBrush="#33000000" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3">
<ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost"/>
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PasswordBoxVistaStyle" BasedOn="{x:Null}" TargetType="{x:Type PasswordBox}">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Width" Value="225"/>
<Setter Property="Height" Value="25"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border x:Name="OuterBorder" BorderBrush="#5AFFFFFF" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
<Border x:Name="InnerBorder" Background="#FFFFFFFF" BorderBrush="#33000000" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3">
<Grid>
<Label x:Name="lblPwd" Content="Password" FontSize="11" VerticalAlignment="Center" Margin="2,0,0,0" FontFamily="Verdana" Foreground="#828385" Padding="0"/>
<ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost"/>
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Visibility" TargetName="lblPwd" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<PasswordBox Style="{StaticResource PasswordBoxVistaStyle}" Margin="169,143,22,0" Name="txtPassword" FontSize="14" TabIndex="2" Height="31" VerticalAlignment="Top" />
Esto puede ayudar a verificarlo con su código. Cuando se aplica al cuadro de contraseña, mostrará la contraseña, que desaparecerá cuando el usuario escriba.
namespace PlaceholderForRichTexxBoxInWPF
{
public MainWindow()
{
InitializeComponent();
Application.Current.MainWindow.WindowState = WindowState.Maximized;// maximize window on load
richTextBox1.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(rtb_GotKeyboardFocus);
richTextBox1.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(rtb_LostKeyboardFocus);
richTextBox1.AppendText("Place Holder");
richTextBox1.Foreground = Brushes.Gray;
}
private void rtb_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (sender is RichTextBox)
{
TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd);
if (textRange.Text.Trim().Equals("Place Holder"))
{
((RichTextBox)sender).Foreground = Brushes.Black;
richTextBox1.Document.Blocks.Clear();
}
}
}
private void rtb_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
//Make sure sender is the correct Control.
if (sender is RichTextBox)
{
//If nothing was entered, reset default text.
TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd);
if (textRange.Text.Trim().Equals(""))
{
((RichTextBox)sender).Foreground = Brushes.Gray;
((RichTextBox)sender).AppendText("Place Holder");
}
}
}
}