Background text in WPF textbox [closed]

-2

Hello I wanted to put a background text on a textbox like the ones that are usually put in the forms (as in the dropbox page) that appears 'Name' in a more clear color and until you write something is not removed. Until now I had put it when I had the focus and it was 'Name' written it was deleted but while it is selected it is empty and the user can forget what field it is

    
asked by jooooooooota 08.06.2017 в 15:23
source

2 answers

4

I think you mean the hint or watermark ...

You can create a watermark that can be added to any textbox through a property. Here is the code for the 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;

   /// <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
    }

The property uses a class called WatermarkAdorner, here is the code:

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
   }

Now you can put a watermark on any TextBox in this way:

<AdornerDecorator>
   <TextBox x:Name="SearchTextBox">
      <controls:WatermarkService.Watermark>
         <TextBlock>escribe algo aqui...</TextBlock>
      </controls:WatermarkService.Watermark>
   </TextBox>
</AdornerDecorator>

The watermark can be anything you want (text, images ...). This watermark can also be used even in ComboBoxes and in ItemControls.

I found this answer in the stackoverflow in English, because A while ago I needed to do the same thing and it worked pretty well for me.

However there is another simpler way in which you only need xaml:

<Grid>
    <TextBox  Width="250"  VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="SearchTermTextBox" Margin="5"/>
    <TextBlock IsHitTestVisible="False" Text="escribe algo aqui..." 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>
    
answered by 08.06.2017 в 15:49
1

Here is another more general alternative:

Put this in the app.xaml . What it does is that it modifies the content of a PlaceHolderTextBox element (more info below) and replaces it with 2 TextBox. One for the text when there is nothing written. And the other would be where the user would write.

<Style x:Key="PlaceHolderStyle" TargetType="components:PlaceHolderTextbox">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="components:PlaceHolderTextbox">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"></RowDefinition>
                                </Grid.RowDefinitions>

                                <TextBox Grid.Row="0" 
                                         Name="PlaceHolderTextBox"
                                         VerticalAlignment="{TemplateBinding VerticalAlignment}"
                                         HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                                         BorderThickness="{TemplateBinding BorderThickness}"
                                         BorderBrush="Transparent"
                                         Padding="{TemplateBinding Padding}"
                                         FontSize="{TemplateBinding FontSize}"
                                         Width="{TemplateBinding Width}" 
                                         Text="{TemplateBinding PlaceHolderText}"
                                         Background="{TemplateBinding Background}"
                                         HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                         VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                         Foreground="{TemplateBinding PlaceHolderForeground}"
                                         Height="{TemplateBinding Height}">
                                </TextBox>

                                <TextBox Grid.Row="0"
                                         Name="InputTextBox"
                                         Background="Transparent"
                                         HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                         VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                         BorderThickness="{TemplateBinding BorderThickness}"
                                         BorderBrush="{TemplateBinding BorderBrush}"
                                         VerticalAlignment="{TemplateBinding VerticalAlignment}"
                                         HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                                         Padding="{TemplateBinding Padding}"
                                         Foreground="{TemplateBinding Foreground}"
                                         FontSize="{TemplateBinding FontSize}"
                                         Width="{TemplateBinding Width}" 
                                         Height="{TemplateBinding Height}"></TextBox>

                            </Grid>

                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

Add this class inside a folder called Components . What makes when the user writes, the textbox that makes the function of placeholder disappear by making the text match the textbox background and only leaving the textbox where the user writes.

    public class PlaceHolderTextbox : UserControl
    {


        public PlaceHolderTextbox()
        {

        }


        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            var placeHolder = FindChild<TextBox>(this, "PlaceHolderTextBox");
            var InputTextBox = FindChild<TextBox>(this, "InputTextBox");

            InputTextBox.TextChanged += (sender, e) =>
            {

                if (InputTextBox.Text?.Length == 0)
                {
                    placeHolder.Foreground = PlaceHolderForeground;
                }
                else
                {
                    placeHolder.Foreground = Background;
                }

            };

        }


        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(PlaceHolderTextbox), new PropertyMetadata(String.Empty));



        public SolidColorBrush PlaceHolderForeground
        {
            get { return (SolidColorBrush)GetValue(PlaceHolderForegroundProperty); }
            set { SetValue(PlaceHolderForegroundProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PlaceHolderForeground.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PlaceHolderForegroundProperty =
            DependencyProperty.Register("PlaceHolderForeground", typeof(SolidColorBrush), typeof(PlaceHolderTextbox), new PropertyMetadata( new SolidColorBrush(Color.FromRgb(204,204,204))));



        public string PlaceHolderText
        {
            get { return (string)GetValue(PlaceHolderTextProperty); }
            set { SetValue(PlaceHolderTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PlaceHolderText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PlaceHolderTextProperty =
            DependencyProperty.Register("PlaceHolderText", typeof(string), typeof(PlaceHolderTextbox), new PropertyMetadata(defaultValue: String.Empty));


        public static T FindChild<T>(DependencyObject parent, string childName)
  where T : DependencyObject
        {
            // Confirm parent and childName are valid. 
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child. 
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;
                    // If the child's name is set for search
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        // if the child's name is of the request name
                        foundChild = (T)child;
                        break;
                    }
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }

            return foundChild;
        }
    }

Here's how to use it:

<Window x:Class="Page1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:namespace:Proyecto2"
        xmlns:components="clr-namespace:namespace:Proyecto2.Components"
        mc:Ignorable="d"
        TitlebarHeight="0"
        Title="Pagina" Height="300" Width="585.366">

        <component:PlaceHolderTextbox
            Style="{StaticResource PlaceHolderStyle}"
            Height="30"
            Width="500"
            Foreground="Black"
            PlaceHolderText="Nombre"
            Background="Grey"
            PlaceHolderForeground="#CCC"
            VerticalContentAlignment="Center"
            >

        </component:PlaceHolderTextbox>

</Window>

More info on FindChild on estackoverflow in groin s.

    
answered by 08.06.2017 в 16:56