WPF控件开发(1) TextBox占位符
2013-06-03 08:57
351 查看
在twitter-bootstrap中有这么一个功能:
Placeholder2
可以看到,附加属性只是对于装饰器的删除和添加,别无其他。
运行看看效果,都还不错,第一种实现所出现的问题也都能解决,但是随即发现一个问题:
当隐藏(设置TextBox的Visibility为Hidden时),发现TextBox隐藏了,但是装饰器还存在。
回想WinForm出现此类情况的解决方案,无非是调用Invalidate之类的,但是存在一个时机的问题。
如果当TextBox的Visibility改变时,能获取通知,上述问题就可以解决,而且直接可以删除改装饰器。
鉴于WPF中的触发器能得到某个属性改变的通知,那么我们自己肯定也能得到。
测试发现,上述问题确实已经解决。
当然,除了可以在装饰器中重绘一次,还可以直接在属性改变时直接删除该装饰器。
这段代码当然直接可以写在OnPlaceholder2Changed事件处理函数中。
所以对于这种实现方式总是心有余悸。
所幸装饰器除了使用OnRender方法,还有别的实现方式。
代码不难理解,就是给TextBox上面又放了一个TextBlock,并且把TextBlock的Visibility属性和TextBox的Visibility属性绑定。
最后附上文中demo下载:http://files.cnblogs.com/nanqi/NanQi.Controls.Placeholder.zip
end.
public static string GetPlaceholder2(DependencyObject obj) { return (string)obj.GetValue(Placeholder2Property); } public static void SetPlaceholder2(DependencyObject obj, string value) { obj.SetValue(Placeholder2Property, value); } public static readonly DependencyProperty Placeholder2Property = DependencyProperty.RegisterAttached("Placeholder2", typeof(string), typeof(TextBoxHelper), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPlaceholder2Changed))); public static void OnPlaceholder2Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBox txt = d as TextBox; if (txt == null || e.NewValue.ToString().Trim().Length == 0) return; RoutedEventHandler loadHandler = null; loadHandler = (s1, e1) => { txt.Loaded -= loadHandler; var lay = AdornerLayer.GetAdornerLayer(txt); if (lay == null) return; Adorner[] ar = lay.GetAdorners(txt); if (ar != null) { for (int i = 0; i < ar.Length; i++) { if (ar[i] is PlaceholderAdorner1) { lay.Remove(ar[i]); } } } if (txt.Text.Length == 0) lay.Add(new PlaceholderAdorner1(txt, e.NewValue.ToString())); }; txt.Loaded += loadHandler; txt.TextChanged += (s1, e1) => { bool isShow = txt.Text.Length == 0; var lay = AdornerLayer.GetAdornerLayer(txt); if (lay == null) return; if (isShow) { lay.Add(new PlaceholderAdorner1(txt, e.NewValue.ToString())); } else { Adorner[] ar = lay.GetAdorners(txt); if (ar != null) { for (int i = 0; i < ar.Length; i++) { if (ar[i] is PlaceholderAdorner1) { lay.Remove(ar[i]); } } } } }; }
Placeholder2
可以看到,附加属性只是对于装饰器的删除和添加,别无其他。
运行看看效果,都还不错,第一种实现所出现的问题也都能解决,但是随即发现一个问题:
当隐藏(设置TextBox的Visibility为Hidden时),发现TextBox隐藏了,但是装饰器还存在。
使用装饰器实现-2
针对上述的问题,首先分析问题原因,首先知道OnRender方法是在UIElement中定义,看方法的注释中,意思似乎是只在布局改变时才调用,从OnRender方面下手几乎不可能。回想WinForm出现此类情况的解决方案,无非是调用Invalidate之类的,但是存在一个时机的问题。
如果当TextBox的Visibility改变时,能获取通知,上述问题就可以解决,而且直接可以删除改装饰器。
鉴于WPF中的触发器能得到某个属性改变的通知,那么我们自己肯定也能得到。
public PlaceholderAdorner1(UIElement ele, string placeholder) : base(ele) { _placeholder = placeholder; var dpd = DependencyPropertyDescriptor.FromProperty(UIElement.VisibilityProperty, typeof(UIElement)); dpd.AddValueChanged(ele, new EventHandler((s1, e1) => { this.InvalidateVisual(); })); }
测试发现,上述问题确实已经解决。
当然,除了可以在装饰器中重绘一次,还可以直接在属性改变时直接删除该装饰器。
var dpd = DependencyPropertyDescriptor.FromProperty(UIElement.VisibilityProperty, typeof(UIElement)); dpd.AddValueChanged(txt, new EventHandler((s1, e1) => { bool isShow = txt.Text.Length == 0 && txt.Visibility == Visibility.Visible; var lay = AdornerLayer.GetAdornerLayer(txt); if (lay == null) return; if (isShow) { lay.Add(new PlaceholderAdorner1(txt, e.NewValue.ToString())); } else { Adorner[] ar = lay.GetAdorners(txt); if (ar != null) { for (int i = 0; i < ar.Length; i++) { if (ar[i] is PlaceholderAdorner1) { lay.Remove(ar[i]); } } } } }));
这段代码当然直接可以写在OnPlaceholder2Changed事件处理函数中。
装饰器的另一种实现方式
由于某次手动生成触发器的时候,发现注册属性改变通知和触发器还是有一定的不同的(具体问题有时间再提)。所以对于这种实现方式总是心有余悸。
所幸装饰器除了使用OnRender方法,还有别的实现方式。
public class PlaceholderAdorner2 : Adorner { private VisualCollection _visCollec; private TextBlock _tb; private TextBox _txt; public PlaceholderAdorner2(UIElement ele, string placeholder) : base(ele) { _txt = ele as TextBox; if (_txt == null) return; Binding bd = new Binding("IsVisible"); bd.Source = _txt; bd.Mode = BindingMode.OneWay; bd.Converter = new BoolToVisibilityConverter(); this.SetBinding(TextBox.VisibilityProperty, bd); _visCollec = new VisualCollection(this); _tb = new TextBlock(); _tb.Style = null; _tb.FontSize = _txt.FontSize; _tb.FontFamily = _txt.FontFamily; _tb.FontWeight = _txt.FontWeight; _tb.FontStretch = _txt.FontStretch; _tb.FontStyle = FontStyles.Italic; _tb.Foreground = Brushes.Gray; _tb.Text = placeholder; _tb.IsHitTestVisible = false; _visCollec.Add(_tb); } protected override int VisualChildrenCount { get { return _visCollec.Count; } } protected override Size ArrangeOverride(Size finalSize) { _tb.Arrange(new Rect(new Point(4, 2), finalSize)); return finalSize; } protected override Visual GetVisualChild(int index) { return _visCollec[index]; } }
代码不难理解,就是给TextBox上面又放了一个TextBlock,并且把TextBlock的Visibility属性和TextBox的Visibility属性绑定。
最后附上文中demo下载:http://files.cnblogs.com/nanqi/NanQi.Controls.Placeholder.zip
end.
相关文章推荐
- WPF控件开发(1) TextBox占位符
- 控件开发之WatermarkTextBox
- wpf控件开发基础(4) -属性系统(3)
- WPF显示常用的几个显示文字控件TextBox, TextBlock, Lable
- Windows 8 Metro 开发学习笔记一:新的TextBox控件与换行符
- Windows Phone开发之控件Grid,TextBox,TextBlock,RadioButton,CheckBox,ListBox简介
- 【WPF开发】无人机HUD (Head Up Display)开源控件
- Revit二次开发之让WPF中的TextBox显示上一次的输入值【附源代码】
- WPF 控件开发之MediaElement
- wpf控件开发基础(2) -属性系统(1)
- WPF控件开发指南 1.2
- [WPF]Show新窗口时TextBox等控件无法输入问题解决方法
- WPF中,一个只能输入Double类型数字的TextBox控件的类
- 使用XAML在WPF项目中承载ArcGIS Engine地图控件开发
- WPF Control Development Unleashed(wpf控件开发揭秘) 章节一 WPF的设计理念
- WPF TextBox 控件获取热键并转为 win32 Keys
- WPF 创建多行 TextBox 控件
- WPF显示常用的几个显示文字控件TextBox, TextBlock, Lable
- WPF控件开发之自定义控件(1)
- [WPF]Show新窗口时TextBox等控件无法输入问题解决方法