Watermark / hint text / placeholder TextBox

后端 未结 30 2531
遇见更好的自我
遇见更好的自我 2020-11-22 02:20

How can I put some text into a TextBox which is removed automatically when user types something in it?

30条回答
  •  温柔的废话
    2020-11-22 02:31

    You can create a watermark that can be added to any TextBox with an Attached Property. Here is the source for the Attached Property:

    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;
    
    /// 
    /// Class that provides the Watermark attached property
    /// 
    public static class WatermarkService
    {
        /// 
        /// Watermark Attached Dependency Property
        /// 
        public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
           "Watermark",
           typeof(object),
           typeof(WatermarkService),
           new FrameworkPropertyMetadata((object)null, new PropertyChangedCallback(OnWatermarkChanged)));
    
        #region Private Fields
    
        /// 
        /// Dictionary of ItemsControls
        /// 
        private static readonly Dictionary itemsControls = new Dictionary();
    
        #endregion
    
        /// 
        /// Gets the Watermark property.  This dependency property indicates the watermark for the control.
        /// 
        ///  to get the property from
        /// The value of the Watermark property
        public static object GetWatermark(DependencyObject d)
        {
            return (object)d.GetValue(WatermarkProperty);
        }
    
        /// 
        /// Sets the Watermark property.  This dependency property indicates the watermark for the control.
        /// 
        ///  to set the property on
        /// value of the property
        public static void SetWatermark(DependencyObject d, object value)
        {
            d.SetValue(WatermarkProperty, value);
        }
    
        /// 
        /// Handles changes to the Watermark property.
        /// 
        ///  that fired the event
        /// A  that contains the event data.
        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
    
        /// 
        /// Handle the GotFocus event on the control
        /// 
        /// The source of the event.
        /// A  that contains the event data.
        private static void Control_GotKeyboardFocus(object sender, RoutedEventArgs e)
        {
            Control c = (Control)sender;
            if (ShouldShowWatermark(c))
            {
                ShowWatermark(c);
            }
            else
            {
                RemoveWatermark(c);
            }
        }
    
        /// 
        /// Handle the Loaded and LostFocus event on the control
        /// 
        /// The source of the event.
        /// A  that contains the event data.
        private static void Control_Loaded(object sender, RoutedEventArgs e)
        {
            Control control = (Control)sender;
            if (ShouldShowWatermark(control))
            {
                ShowWatermark(control);
            }
        }
    
        /// 
        /// Event handler for the items source changed event
        /// 
        /// The source of the event.
        /// A  that contains the event data.
        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);
            }
        }
    
        /// 
        /// Event handler for the items changed event
        /// 
        /// The source of the event.
        /// A  that contains the event data.
        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
    
        /// 
        /// Remove the watermark from the specified element
        /// 
        /// Element to remove the watermark from
        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);
                    }
                }
            }
        }
    
        /// 
        /// Show the watermark on the specified control
        /// 
        /// Control to show the watermark on
        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)));
            }
        }
    
        /// 
        /// Indicates whether or not the watermark should be shown on the specified control
        /// 
        ///  to test
        /// true if the watermark should be shown; false otherwise
        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
    }
    

    The Attached Property uses a class called WatermarkAdorner, here is that source:

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    /// 
    /// Adorner for the watermark
    /// 
    internal class WatermarkAdorner : Adorner
    {
        #region Private Fields
    
        /// 
        ///  that holds the watermark
        /// 
        private readonly ContentPresenter contentPresenter;
    
        #endregion
    
        #region Constructor
    
        /// 
        /// Initializes a new instance of the  class
        /// 
        ///  to be adorned
        /// The watermark
        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
    
        /// 
        /// Gets the number of children for the .
        /// 
        protected override int VisualChildrenCount
        {
            get { return 1; }
        }
    
        #endregion
    
        #region Private Properties
    
        /// 
        /// Gets the control that is being adorned
        /// 
        private Control Control
        {
            get { return (Control)this.AdornedElement; }
        }
    
        #endregion
    
        #region Protected Overrides
    
        /// 
        /// Returns a specified child  for the parent .
        /// 
        /// A 32-bit signed integer that represents the index value of the child . The value of index must be between 0 and  - 1.
        /// The child .
        protected override Visual GetVisualChild(int index)
        {
            return this.contentPresenter;
        }
    
        /// 
        /// Implements any custom measuring behavior for the adorner.
        /// 
        /// A size to constrain the adorner to.
        /// A  object representing the amount of layout space needed by the adorner.
        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;
        }
    
        /// 
        /// When overridden in a derived class, positions child elements and determines a size for a  derived class. 
        /// 
        /// The final area within the parent that this element should use to arrange itself and its children.
        /// The actual size used.
        protected override Size ArrangeOverride(Size finalSize)
        {
            this.contentPresenter.Arrange(new Rect(finalSize));
            return finalSize;
        }
    
        #endregion
    }
    

    Now you can put a watermark on any TextBox like this:

    
       
          
             Type here to search text
          
       
    
    

    The watermark can be anything you want (text, images ...). In addition to working for TextBoxes, this watermark also works for ComboBoxes and ItemControls.

    This code was adapted from this blog post.

提交回复
热议问题