带有水印文本框的附加属性 [英] Attached Property with WaterMark Textbox

查看:26
本文介绍了带有水印文本框的附加属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个带有 WaterMark 文本的 TextBox.我使用这个解决方案,其中按规定工作正常.

I want a TextBox with a WaterMark text. I use this solution, which works fine as provided.

因为我在控件中有几个文本框,所以我想让它有点动态.所以我(第一次)使用了一个附加属性,但我无法让它工作.没有编译错误,但在 XAML 中无法解析标记语句 ... Content={Binding Path=view:SomeClass.Tag, RelativeSource=...

Since I have a couple TextBoxes in the control I want to make it a bit dynamic. So I use ( the first time) an attached property, but I can't make it work. No compile errors, but in XAML the Tag Statement can not be resolved ... Content={Binding Path=view:SomeClass.Tag, RelativeSource=...

这里出了什么问题?

我在 XAML 中做到了这一点

I did this in XAML

    <StackPanel Grid.Row="1" TextBlock.FontSize="12">
        <TextBox Style="{DynamicResource TextBoxWaterMark}" view:SomeClass.Tag="Search" />
    </StackPanel>

风格

<RibbonWindow.Resources>
    <Style xmlns:sys="clr-namespace:System;assembly=mscorlib"
           x:Key="TextBoxWaterMark"
           TargetType="{x:Type TextBox}">
        <Style.Resources>
            <VisualBrush x:Key="CueBannerBrush"
                         AlignmentX="Left"
                         AlignmentY="Center"
                         Stretch="None">
                <VisualBrush.Visual>
                    <Label Content="{Binding Path=view:SomeClass.Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type view:SomeClass}}}" 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="IsMouseCaptured" Value="True">
                <Setter Property="Background" Value="White" />
            </Trigger>
        </Style.Triggers>
    </Style>
</RibbonWindow.Resources>

依赖对象

public static class SomeClass
{
    public static readonly DependencyProperty TagProperty = DependencyProperty.RegisterAttached(
        "Tag",
        typeof(object),
        typeof(SomeClass),
        new FrameworkPropertyMetadata(null));

    public static object GetTag(DependencyObject dependencyObject)
    {
        return dependencyObject.GetValue(TagProperty);
    }

    public static void SetTag(DependencyObject dependencyObject, object value)
    {
        dependencyObject.SetValue(TagProperty, value);
    }
}

推荐答案

您可以创建此类附加属性.在这里看看如何.

You Can create such type attached property. here see how.

大多数人都在寻找水面罩文本框,组合项控件等等.让我们一次性涵盖所有这些.

Mostly peoples looking for watermask textboxs what about combobo items controls etc. lets cover these all at once.

创建 AttachedProperty 之类的.

create AttachedProperty like .

     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 || d is TextBox)
             {
                control.GotKeyboardFocus += Control_GotKeyboardFocus;
                control.LostKeyboardFocus += Control_Loaded;
             }

             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))
             {
                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
       }

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
   }

使用此附加属性的示例.

Sample to Use this attached Property.

<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:local="clr-namespace:WpfApplication1"
        Height="403" 
        Width="346" 
        Title="Window1">   
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <AdornerDecorator Grid.Row="0">
            <TextBox  VerticalAlignment="Center" >
                <local:WatermarkService.Watermark>
                    <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12">TextBox Water Mask </TextBlock>
                </local:WatermarkService.Watermark>
            </TextBox>
        </AdornerDecorator>
        <AdornerDecorator Grid.Row="1">
            <ComboBox  ItemsSource="{Binding Items}">
                <local:WatermarkService.Watermark>
                   <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12">Combo Box WaterMask</TextBlock>
                </local:WatermarkService.Watermark>
            </ComboBox>
        </AdornerDecorator>     
    </Grid>
</Window>

这篇关于带有水印文本框的附加属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆