我怎么能把一些文本放入一个文本框,这将被自动删除时,用户键入的东西在它?
当前回答
MahApps。Metro for WPF有一个内置的水印控制,如果你不想自己滚动。使用起来相当简单。
<AdornerDecorator>
<TextBox Name="txtSomeText"
Width="200"
HorizontalAlignment="Right">
<Controls:TextBoxHelper.Watermark>I'm a watermark!</Controls:TextBoxHelper.Watermark>
</TextBox>
</AdornerDecorator>
其他回答
<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();
}
这将创建两个文本框,一个用于接收水印的字段,另一个用于水印。接收水印的字段可以是ComboBox或其他控件(只要您获得正确的对齐并相应地获得焦点)。
水印必须是一个文本框或其他控制,可以获得焦点(TextBlock不能获得焦点,因为我学会了艰难的方式)。当水印文本框得到焦点,它隐藏自己,并给予访问其他文本框。
如果你想在搜索栏文本框失去焦点后重新显示水印,只需添加一个LostFocus事件处理程序到TextBox_SearchBar检查输入并切换TextBox_Watermark可见性为隐藏或可见,相应地。
如果水印的可见性不是取决于控件的焦点状态,而是取决于用户是否输入了任何文本,您可以将John Myczek的答案(从OnWatermarkChanged向下)更新为
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);
}
}
}
}
}
如果文本框在显示表单或绑定到Text属性时自动获得焦点,这就更有意义了。
同样,如果你的水印总是一个字符串,你需要水印的风格来匹配文本框的风格,那么在Adorner做:
contentPresenter = new ContentPresenter {
Content = new TextBlock {
Text = (string)watermark,
Foreground = Control.Foreground,
Background = Control.Background,
FontFamily = Control.FontFamily,
FontSize = Control.FontSize,
...
},
...
}
您可以为输入的文本保留一个单独的值,并且可以在“GotFocus”和“LostFocus”事件中设置文本框的“text”字段。当您获得焦点时,如果没有值,则需要清除文本框。当你失去焦点时,你会想要设置从文本框中获取“文本”值,然后将文本框的“文本”值重置为占位符(如果它是空的)。
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";
}
}
然后,您只需确保文本框的“Text”值初始化为占位符文本。
<TextBox x:Name="usernameTextBox" Text="Username" GotFocus="usernameTextBox_GotFocus" LostFocus="usernameTextBox_LostFocus" />
您可以进一步将其提取到扩展“TextBox”类的类中,然后在整个项目中重用它。
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);
}
}
}
然后这个可以直接添加到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>
您可以创建水印,可以添加到任何具有附加属性的文本框。下面是附件属性的来源:
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
}
附加属性使用一个叫做WatermarkAdorner的类,这里是那个来源:
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
}
现在你可以像这样把水印放在任何文本框上:
<AdornerDecorator>
<TextBox x:Name="SearchTextBox">
<controls:WatermarkService.Watermark>
<TextBlock>Type here to search text</TextBlock>
</controls:WatermarkService.Watermark>
</TextBox>
</AdornerDecorator>
水印可以是你想要的任何东西(文本、图像……)。除了适用于文本框,这个水印也适用于组合框和项目控件。
此代码改编自这篇博客文章。
@Veton -我真的很喜欢你的解决方案的简单性,但我的声誉还没有高到足以撞到你。
@Tim Murphy -“双向绑定需要Path或XPath”的错误很容易修复…更新的代码,包括一些其他的小调整(仅WPF测试):
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);
}
}
}
推荐文章
- dynamic (c# 4)和var之间的区别是什么?
- Visual Studio: ContextSwitchDeadlock
- 返回文件在ASP。Net Core Web API
- 自定义HttpClient请求头
- 如果我使用OWIN Startup.cs类并将所有配置移动到那里,我是否需要一个Global.asax.cs文件?
- VS2013外部构建错误"error MSB4019: The imported project <path> was not found"
- 从另一个列表id中排序一个列表
- 等待一个无效的异步方法
- 无法加载文件或程序集…参数不正确
- c#中枚举中的方法
- 如何从字符串中删除新的行字符?
- 如何设置一个默认值与Html.TextBoxFor?
- 检查属性是否有属性
- 格式化XML字符串以打印友好的XML字符串
- 返回内容与IHttpActionResult非ok响应