水印控件

用装饰器实现,装饰器最大的好处就是不影响控件原本的表现的基础之上增加一些显示的效果,而且利于扩展。

通过扩展控件也能实现,但是部分控件是密封的,比如PasswordBox,而且还需要修改样式是比较麻烦费力;

效果

 

 

WatermarkAdorner:定义了附加属性、在Text的附加属性的OnTextChanged中给对应的控件的加载事件添加了Adorner(利用反射消除控件类型及对应的控件装饰器的判断)

    /// <summary>
    /// 水印装饰器 
    /// </summary>
    public class WatermarkAdorner : Adorner
    {
        public WatermarkAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
            IsHitTestVisible = false; //不可命中
            InvalidateVisual();
        }

        //强制布局发生改变,根据当前信息实时绘制
        protected void Adorner_RoutedEvent(object sender, RoutedEventArgs e)
        {
            InvalidateVisual();
        }

        #region 附加属性

        public static Thickness GetMargin(DependencyObject obj)
        {
            return (Thickness)obj.GetValue(MarginProperty);
        }

        public static void SetMargin(DependencyObject obj, Thickness value)
        {
            obj.SetValue(MarginProperty, value);
        }

        public new readonly static DependencyProperty MarginProperty =
            DependencyProperty.RegisterAttached("Margin", typeof(Thickness), typeof(WatermarkAdorner), new PropertyMetadata(new Thickness(0)));

        public static string GetText(DependencyObject obj)
        {
            return (string)obj.GetValue(TextProperty);
        }

        public static void SetText(DependencyObject obj, string value)
        {
            obj.SetValue(TextProperty, value);
        }

        /// <summary>
        /// 文本属性
        /// </summary>
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.RegisterAttached("Text", typeof(string), typeof(WatermarkAdorner), new PropertyMetadata(string.Empty, OnTextChanged));

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as FrameworkElement;
            if (element != null)
            {
                WatermarkAdorner adorner = new WatermarkAdorner(element);
                WatermarkAdorner watermarkAdorner = null;
                Type type = adorner.GetType();
                Assembly assembly = type.Assembly;
                watermarkAdorner = (WatermarkAdorner)assembly.CreateInstance(type.Namespace + "." + element.GetType().Name + "WatermarkAdorner", false, BindingFlags.CreateInstance, null, new object[] { element }, CultureInfo.CurrentCulture, null);

                element.Loaded += (sender, arg) =>
                {
                    if (watermarkAdorner != null)
                    {
                        AdornerLayer.GetAdornerLayer(element).Add(watermarkAdorner);
                    }
                };
            }
        }

        public static Brush GetForeground(DependencyObject obj)
        {
            return (Brush)obj.GetValue(ForegroundProperty);
        }

        public static void SetForeground(DependencyObject obj, Brush value)
        {
            obj.SetValue(ForegroundProperty, value);
        }

        /// <summary>
        /// 前景色
        /// </summary>
        public static readonly DependencyProperty ForegroundProperty =
            DependencyProperty.RegisterAttached("Foreground", typeof(Brush), typeof(WatermarkAdorner), new UIPropertyMetadata(Brushes.Gray));

        public static Brush GetBackground(DependencyObject obj)
        {
            return (Brush)obj.GetValue(BackgroundProperty);
        }

        public static void SetBackground(DependencyObject obj, Brush value)
        {
            obj.SetValue(BackgroundProperty, value);
        }

        /// <summary>
        /// 背景色
        /// </summary>
        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.RegisterAttached("Background", typeof(Brush), typeof(WatermarkAdorner), new UIPropertyMetadata(Brushes.Transparent));


        public static FontStyle GetFontStyle(DependencyObject obj)
        {
            return (FontStyle)obj.GetValue(FontStyleProperty);
        }

        public static void SetFontStyle(DependencyObject obj, FontStyle value)
        {
            obj.SetValue(FontStyleProperty, value);
        }

        /// <summary>
        /// 字体风格
        /// </summary>
        public static readonly DependencyProperty FontStyleProperty =
            DependencyProperty.RegisterAttached("FontStyle", typeof(FontStyle), typeof(WatermarkAdorner), new UIPropertyMetadata(FontStyles.Italic));

        #endregion
    }
}

 


TextBoxWatermarkAdorner:构造函数为控件的对应事件订阅刷新水印事件、OnRender方法判断是否满足添加水印的条件以及绘制水印

    public class TextBoxWatermarkAdorner : WatermarkAdorner
    {

        //被装饰的文本输入框
        private readonly TextBox _adornerTextBox;
        public TextBoxWatermarkAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
            var adornerTextBox = adornedElement as TextBox;
            if (adornerTextBox != null)
            {
                _adornerTextBox = adornerTextBox;
                _adornerTextBox.LostFocus += Adorner_RoutedEvent;
                _adornerTextBox.GotFocus += Adorner_RoutedEvent;
                _adornerTextBox.TextChanged += Adorner_RoutedEvent;
            }
            InvalidateVisual();
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            if (_adornerTextBox != null && (!_adornerTextBox.IsFocused && string.IsNullOrEmpty(_adornerTextBox.Text)))
            {
                FormattedText formattedText =
                    new FormattedText(
                        GetText(_adornerTextBox),
                        CultureInfo.CurrentUICulture,
                        _adornerTextBox.FlowDirection,
                        _adornerTextBox.FontFamily.GetTypefaces().FirstOrDefault(),
                        _adornerTextBox.FontSize,
                        GetForeground(_adornerTextBox));
                formattedText.SetFontStyle(GetFontStyle(_adornerTextBox));

                drawingContext.DrawText(formattedText,
                    new Point(GetMargin(_adornerTextBox).Left, GetMargin(_adornerTextBox).Left));
            }
        }
    }

 

 

PasswordBoxWatermarkAdorner:构造函数为控件的对应事件订阅刷新水印事件、OnRender方法判断是否满足添加水印的条件以及绘制水印

 

    public class PasswordBoxWatermarkAdorner : WatermarkAdorner
    {
        private readonly PasswordBox _adornedPasswordBox;

        public PasswordBoxWatermarkAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
            var passwordBox = adornedElement as PasswordBox;
            if (passwordBox != null)
            {
                _adornedPasswordBox = passwordBox;
                _adornedPasswordBox.LostFocus += Adorner_RoutedEvent;
                _adornedPasswordBox.GotFocus += Adorner_RoutedEvent;
                _adornedPasswordBox.PasswordChanged += Adorner_RoutedEvent;
            }
            InvalidateVisual();
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            if (_adornedPasswordBox != null && (_adornedPasswordBox.IsFocused || !string.IsNullOrEmpty(_adornedPasswordBox.Password)))
                return;

            FormattedText formatted = new FormattedText(GetText(_adornedPasswordBox), CultureInfo.CurrentCulture,
                GetFlowDirection(_adornedPasswordBox), _adornedPasswordBox.FontFamily.GetTypefaces().FirstOrDefault(),
                _adornedPasswordBox.FontSize, GetForeground(_adornedPasswordBox));

            formatted.SetFontStyle(GetFontStyle(_adornedPasswordBox));

            drawingContext.DrawText(formatted,
                new Point(GetMargin(_adornedPasswordBox).Left, GetMargin(_adornedPasswordBox).Top));
        }
    }

 


界面使用

<Window x:Class="Watermarker.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Watermarker"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBox Height="40" Width="100"
                 FontSize="18"
                 local:TextBoxWatermarkAdorner.Text="请输入文本"
                 local:TextBoxWatermarkAdorner.Margin="5,5,0,0"/>
        <PasswordBox Height="40" Width="100"
                     local:PasswordBoxWatermarkAdorner.Text="请输入密码"
                     local:PasswordBoxWatermarkAdorner.Margin="5,5,0,0"/>
    </StackPanel>
</Window>

 

2016/07/05

发现一处Bug:

  AdornerLayer.GetAdornerLayer(element)可能存在返回值为空的状况,那么程序会直接崩溃,不够友好,这里是比较粗心大意的一个写代码陋习,在使用引用对象的时候没有检查值是否存在。
  1     public class WatermarkAdorner : Adorner
  2     {
  3         public WatermarkAdorner(UIElement adornedElement)
  4             : base(adornedElement)
  5         {
  6             IsHitTestVisible = false; //不可命中
  7             InvalidateVisual();
  8         }
  9 
 10         //强制布局发生改变,根据当前信息实时绘制
 11         protected void Adorner_RoutedEvent(object sender, RoutedEventArgs e)
 12         {
 13             InvalidateVisual();
 14         }
 15 
 16         #region 附加属性
 17 
 18         public static Thickness GetMargin(DependencyObject obj)
 19         {
 20             return (Thickness)obj.GetValue(MarginProperty);
 21         }
 22 
 23         public static void SetMargin(DependencyObject obj, Thickness value)
 24         {
 25             obj.SetValue(MarginProperty, value);
 26         }
 27 
 28         public new readonly static DependencyProperty MarginProperty =
 29             DependencyProperty.RegisterAttached("Margin", typeof(Thickness), typeof(WatermarkAdorner), new PropertyMetadata(new Thickness(0)));
 30 
 31         public static string GetText(DependencyObject obj)
 32         {
 33             return (string)obj.GetValue(TextProperty);
 34         }
 35 
 36         public static void SetText(DependencyObject obj, string value)
 37         {
 38             obj.SetValue(TextProperty, value);
 39         }
 40 
 41         /// <summary>
 42         /// 文本属性
 43         /// </summary>
 44         public static readonly DependencyProperty TextProperty =
 45             DependencyProperty.RegisterAttached("Text", typeof(string), typeof(WatermarkAdorner), new PropertyMetadata(string.Empty, OnTextChanged));
 46 
 47         private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
 48         {
 49             var element = d as FrameworkElement;
 50             if (element != null)
 51             {
 52                 WatermarkAdorner adorner = new WatermarkAdorner(element);
 53                 WatermarkAdorner watermarkAdorner = null;
 54                 Type type = adorner.GetType();
 55                 Assembly assembly = type.Assembly;
 56                 watermarkAdorner = (WatermarkAdorner)assembly.CreateInstance(type.Namespace + "." + element.GetType().Name + "WatermarkAdorner", false, BindingFlags.CreateInstance, null, new object[] { element }, CultureInfo.CurrentCulture, null);
 57 
 58                 element.Loaded += (sender, arg) =>
 59                 {
 60                     if (watermarkAdorner != null)
 61                     {
 62                         var adornerLayer = GetAdornerLayer(element);
 63                         if (adornerLayer != null)
 64                         {
 65                             adornerLayer.Add(watermarkAdorner);
 66                         }
 67                     }
 68                 };
 69             }
 70         }
 71 
 72         private static AdornerLayer GetAdornerLayer(FrameworkElement element)
 73         {
 74             if (element == null) return null;
 75 
 76             AdornerLayer layer = AdornerLayer.GetAdornerLayer(element);
 77             var parent = element.Parent as FrameworkElement;
 78             while (layer == null)
 79             {
 80                 if (parent != null)
 81                 {
 82                     layer = AdornerLayer.GetAdornerLayer(parent);
 83                     parent = parent.Parent as FrameworkElement;
 84                 }
 85                 else break;
 86             }
 87 
 88             return layer;
 89         }
 90 
 91         public static Brush GetForeground(DependencyObject obj)
 92         {
 93             return (Brush)obj.GetValue(ForegroundProperty);
 94         }
 95 
 96         public static void SetForeground(DependencyObject obj, Brush value)
 97         {
 98             obj.SetValue(ForegroundProperty, value);
 99         }
100 
101         /// <summary>
102         /// 前景色
103         /// </summary>
104         public static readonly DependencyProperty ForegroundProperty =
105             DependencyProperty.RegisterAttached("Foreground", typeof(Brush), typeof(WatermarkAdorner), new UIPropertyMetadata(Brushes.Gray));
106 
107         public static Brush GetBackground(DependencyObject obj)
108         {
109             return (Brush)obj.GetValue(BackgroundProperty);
110         }
111 
112         public static void SetBackground(DependencyObject obj, Brush value)
113         {
114             obj.SetValue(BackgroundProperty, value);
115         }
116 
117         /// <summary>
118         /// 背景色
119         /// </summary>
120         public static readonly DependencyProperty BackgroundProperty =
121             DependencyProperty.RegisterAttached("Background", typeof(Brush), typeof(WatermarkAdorner), new UIPropertyMetadata(Brushes.Transparent));
122 
123 
124         public static FontStyle GetFontStyle(DependencyObject obj)
125         {
126             return (FontStyle)obj.GetValue(FontStyleProperty);
127         }
128 
129         public static void SetFontStyle(DependencyObject obj, FontStyle value)
130         {
131             obj.SetValue(FontStyleProperty, value);
132         }
133 
134         /// <summary>
135         /// 字体风格
136         /// </summary>
137         public static readonly DependencyProperty FontStyleProperty =
138             DependencyProperty.RegisterAttached("FontStyle", typeof(FontStyle), typeof(WatermarkAdorner), new UIPropertyMetadata(FontStyles.Italic));
139 
140         #endregion
141     }

2016.8.16添加,用于WatermarkAdorner在无法获取AdornerLayer的场景下获取AdornerLayer

 
posted @ 2015-11-05 17:44  蘑菇mr  阅读(468)  评论(1编辑  收藏  举报