简体   繁体   English

带WaterMark文本框的附加属性

[英]Attached Property with WaterMark Textbox

I want a TextBox with a WaterMark text. 我想要一个带有WaterMark文本的TextBox。 I use this solution, which works fine as provided. 我使用解决方案, 解决方案按规定工作正常。

Since I have a couple TextBoxes in the control I want to make it a bit dynamic. 由于控件中有几个TextBoxes,因此我想使其变得动态。 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中无法解析Tag语句... Content={Binding Path=view:SomeClass.Tag, RelativeSource=...

What is wrong here? 怎么了

I did this in XAML 我在XAML中做到了

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

Style 样式

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

DependencyObject 依赖对象

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. 通常,人们在寻找水面罩文本框,而组合项控件等又如何让它们一次覆盖所有这些。

create AttachedProperty like . 创建AttachedProperty之类的。

     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>

For attached properties you need to use parentheses in your binding path: 对于附加属性,您需要在绑定路径中使用括号:

<Label Content="{Binding Path=(view:SomeClass.Tag)}" />

This is written here along with explanations on how to bind to other types, such as indexers and collection views. 这是写在这里就如何绑定到其他类型,如索引和收集意见的说明一起。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM