使用WaterMark文本框附加属性

时间:2013-09-17 07:24:55

标签: c# wpf textbox

我想要一个带有WaterMark文本的TextBox。我使用this解决方案,它可以正常工作。

由于我在控件中有几个TextBox,我想让它有点动态。所以我使用(第一次)附加属性,但我不能使它工作。没有编译错误,但在XAML中无法解析Tag语句... Content={Binding Path=view:SomeClass.Tag, RelativeSource=...

这里有什么问题?

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

的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);
    }
}

2 个答案:

答案 0 :(得分:1)

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

大多数人都在寻找watermask文本框,关于combobo项目控件等等,我们可以同时覆盖这些内容。

创建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
   }

使用此附加属性的示例。

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

答案 1 :(得分:0)

对于附加属性,您需要在绑定路径中使用括号:

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

这是写here以及如何绑定到其他类型的解释,例如索引器和集合视图。