- 浏览: 84428 次
文章分类
- 全部博客 (136)
- 我的技术资料收集 (98)
- 具体技术 (1)
- 的技术资料收集 (4)
- All Articles (1)
- 机器学习 Machine Learning (1)
- 网络编程 (1)
- java (2)
- ava (1)
- 零散技术 (1)
- C# (3)
- 技术资料收集 (1)
- CQRS (1)
- 数据库技术(MS SQL) (1)
- .Net微观世界 (1)
- Oracle SQL学习之路 (1)
- C/C++ (1)
- JS/JQ (1)
- Js封装的插件/实例/方法 (2)
- 敏捷个人 (2)
- Javascript (1)
- 程序设计---设计模式 (1)
- Bug (1)
- 未知分类 (1)
- 程序设计 (1)
- Sharepoint (1)
- Computer Graphic (1)
- IT产品 (1)
- [06]JS/jQuery (1)
- [07]Web开发 (1)
- .NET Solution (1)
- Android (3)
- 机器学习 (1)
- 系统框架设计 (1)
- Others (1)
- 算法 (1)
- 基于Oracle Logminer数据同步 (1)
- 网页设计 (1)
- 原创翻译 (1)
- EXTJS (1)
- Jqgrid (1)
- 云计算 (1)
最新评论
在twitter-bootstrap中有这么一个功能:
我们如何在WPF也实现类似这种写法:
<TextBox local:placeholder="请输入筛选条件..." />
首先熟悉一点WPF的人都知道,placeholder在这里是一个附加属性,而这个附加属性的类型是String。
第一种实现方式
首先我们想到的可能是这样:
1 public static string GetPlaceholder1(DependencyObject obj)
2 {
3 return (string)obj.GetValue(Placeholder1Property);
4 }
5 public static void SetPlaceholder1(DependencyObject obj, string value)
6 {
7 obj.SetValue(Placeholder1Property, value);
8 }
9 public static readonly DependencyProperty Placeholder1Property =
10 DependencyProperty.RegisterAttached("Placeholder1", typeof(string), typeof(TextBoxHelper),
11 new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPlaceholder1Changed)));
12 public static void OnPlaceholder1Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
13 {
14 TextBox txt = d as TextBox;
15 if (txt == null || e.NewValue.ToString().Trim().Length == 0) return;
16 RoutedEventHandler loadHandler = null;
17 loadHandler = (s1, e1) =>
18 {
19 txt.Loaded -= loadHandler;
20 if (txt.Text.Length == 0)
21 {
22 txt.Text = e.NewValue.ToString();
23 txt.FontStyle = FontStyles.Italic;
24 txt.Foreground = Brushes.Gray;
25 }
26 };
27 txt.Loaded += loadHandler;
28 txt.GotFocus += (s1, e1) =>
29 {
30 if (txt.Text == e.NewValue.ToString())
31 {
32 txt.Clear();
33 txt.FontStyle = FontStyles.Normal;
34 txt.Foreground = SystemColors.WindowTextBrush;
35 }
36 };
37 txt.LostFocus += (s1, e1) =>
38 {
39 if (txt.Text.Length == 0)
40 {
41 txt.Text = e.NewValue.ToString();
42 txt.FontStyle = FontStyles.Italic;
43 txt.Foreground = Brushes.Gray;
44 }
45 };
46 }
基本存在以下几个问题:
- 获得焦点取消了占位符,与需求(twitter-bootstrap)不符,这里需要当输入内容以后才取消占位符;
- 如果这里某人恰巧也输入了”请输入筛选条件...”,当再次获得焦点的时候就会清空输入的内容,当然这一条完全可以再给一个标示去判断,比如在GotFocus的判断中,增加一个:
-
1 txt.GotFocus += (s1, e1) =>
2 {
3 if (txt.Text == e.NewValue.ToString()
4 && txt.FontStyle == FontStyles.Italic
5 && txt.Foreground == Brushes.Gray)
6 {
7 txt.Clear();
8 txt.FontStyle = FontStyles.Normal;
9 txt.Foreground = SystemColors.WindowTextBrush;
10 }
11 };
- 接之而来的问题就是如果TextBox恰巧需要设置FontStyle或Foreground,此时就无能为力;
- 其实最重要的问题还是,当我没有输入内容时,获取TextBox的Text属性,总有一个我不需要的值,或许加个判断可以搞定,但是这不是一个好的方式;
使用装饰器实现-1
上面实现方式所带来的弊端,可以使用装饰器解决。
首先定义一个装饰器,它可能是这样:
1 public class PlaceholderAdorner1 : Adorner
2 {
3 string _placeholder;
4 public PlaceholderAdorner1(UIElement ele, string placeholder)
5 : base(ele)
6 {
7 _placeholder = placeholder;
8 }
9 protected override void OnRender(DrawingContext drawingContext)
10 {
11 TextBox txt = this.AdornedElement as TextBox;
12 if (txt == null || !txt.IsVisible || string.IsNullOrEmpty(_placeholder)) return;
13 this.IsHitTestVisible = false;
14 drawingContext.DrawText(
15 new FormattedText
16 (
17 _placeholder,
18 CultureInfo.CurrentCulture,
19 txt.FlowDirection,
20 new Typeface(txt.FontFamily, FontStyles.Italic, txt.FontWeight, txt.FontStretch),
21 txt.FontSize,
22 Brushes.Gray
23 ),
24 new Point(4, 2));
25 }
26 }
思路很简单,记录下这个占位符,然后给TextBox指定位置画出来。
这时候,附加属性可能是这样:
1 public static string GetPlaceholder2(DependencyObject obj)
2 {
3 return (string)obj.GetValue(Placeholder2Property);
4 }
5 public static void SetPlaceholder2(DependencyObject obj, string value)
6 {
7 obj.SetValue(Placeholder2Property, value);
8 }
9 public static readonly DependencyProperty Placeholder2Property =
10 DependencyProperty.RegisterAttached("Placeholder2", typeof(string), typeof(TextBoxHelper),
11 new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPlaceholder2Changed)));
12 public static void OnPlaceholder2Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
13 {
14 TextBox txt = d as TextBox;
15 if (txt == null || e.NewValue.ToString().Trim().Length == 0) return;
16 RoutedEventHandler loadHandler = null;
17 loadHandler = (s1, e1) =>
18 {
19 txt.Loaded -= loadHandler;
20 var lay = AdornerLayer.GetAdornerLayer(txt);
21 if (lay == null) return;
22 Adorner[] ar = lay.GetAdorners(txt);
23 if (ar != null)
24 {
25 for (int i = 0; i < ar.Length; i++)
26 {
27 if (ar[i] is PlaceholderAdorner1)
28 {
29 lay.Remove(ar[i]);
30 }
31 }
32 }
33 if (txt.Text.Length == 0)
34 lay.Add(new PlaceholderAdorner1(txt, e.NewValue.ToString()));
35 };
36 txt.Loaded += loadHandler;
37 txt.TextChanged += (s1, e1) =>
38 {
39 bool isShow = txt.Text.Length == 0;
40 var lay = AdornerLayer.GetAdornerLayer(txt);
41 if (lay == null) return;
42 if (isShow)
43 {
44 lay.Add(new PlaceholderAdorner1(txt, e.NewValue.ToString()));
45 }
46 else
47 {
48 Adorner[] ar = lay.GetAdorners(txt);
49 if (ar != null)
50 {
51 for (int i = 0; i < ar.Length; i++)
52 {
53 if (ar[i] is PlaceholderAdorner1)
54 {
55 lay.Remove(ar[i]);
56 }
57 }
58 }
59 }
60 };
61 }
可以看到,附加属性只是对于装饰器的删除和添加,别无其他。
运行看看效果,都还不错,第一种实现所出现的问题也都能解决,但是随即发现一个问题:
- 当隐藏(设置TextBox的Visibility为Hidden时),发现TextBox隐藏了,但是装饰器还存在。
使用装饰器实现-2
针对上述的问题,首先分析问题原因,首先知道OnRender方法是在UIElement中定义,看方法的注释中,意思似乎是只在布局改变时才调用,从OnRender方面下手几乎不可能。
回想WinForm出现此类情况的解决方案,无非是调用Invalidate之类的,但是存在一个时机的问题。
如果当TextBox的Visibility改变时,能获取通知,上述问题就可以解决,而且直接可以删除改装饰器。
鉴于WPF中的触发器能得到某个属性改变的通知,那么我们自己肯定也能得到。
1 public PlaceholderAdorner1(UIElement ele, string placeholder)
2 : base(ele)
3 {
4 _placeholder = placeholder;
5 var dpd = DependencyPropertyDescriptor.FromProperty(UIElement.VisibilityProperty, typeof(UIElement));
6 dpd.AddValueChanged(ele, new EventHandler((s1, e1) =>
7 {
8 this.InvalidateVisual();
9 }));
10 }
测试发现,上述问题确实已经解决。
当然,除了可以在装饰器中重绘一次,还可以直接在属性改变时直接删除该装饰器。
1 var dpd = DependencyPropertyDescriptor.FromProperty(UIElement.VisibilityProperty, typeof(UIElement));
2 dpd.AddValueChanged(txt, new EventHandler((s1, e1) =>
3 {
4 bool isShow = txt.Text.Length == 0 && txt.Visibility == Visibility.Visible;
5 var lay = AdornerLayer.GetAdornerLayer(txt);
6 if (lay == null) return;
7 if (isShow)
8 {
9 lay.Add(new PlaceholderAdorner1(txt, e.NewValue.ToString()));
10 }
11 else
12 {
13 Adorner[] ar = lay.GetAdorners(txt);
14 if (ar != null)
15 {
16 for (int i = 0; i < ar.Length; i++)
17 {
18 if (ar[i] is PlaceholderAdorner1)
19 {
20 lay.Remove(ar[i]);
21 }
22 }
23 }
24 }
25 }));
这段代码当然直接可以写在OnPlaceholder2Changed事件处理函数中。
装饰器的另一种实现方式
由于某次手动生成触发器的时候,发现注册属性改变通知和触发器还是有一定的不同的(具体问题有时间再提)。
所以对于这种实现方式总是心有余悸。
所幸装饰器除了使用OnRender方法,还有别的实现方式。
1 public class PlaceholderAdorner2 : Adorner
2 {
3 private VisualCollection _visCollec;
4 private TextBlock _tb;
5 private TextBox _txt;
6 public PlaceholderAdorner2(UIElement ele, string placeholder)
7 : base(ele)
8 {
9 _txt = ele as TextBox;
10 if (_txt == null) return;
11 Binding bd = new Binding("IsVisible");
12 bd.Source = _txt;
13 bd.Mode = BindingMode.OneWay;
14 bd.Converter = new BoolToVisibilityConverter();
15 this.SetBinding(TextBox.VisibilityProperty, bd);
16 _visCollec = new VisualCollection(this);
17 _tb = new TextBlock();
18 _tb.Style = null;
19 _tb.FontSize = _txt.FontSize;
20 _tb.FontFamily = _txt.FontFamily;
21 _tb.FontWeight = _txt.FontWeight;
22 _tb.FontStretch = _txt.FontStretch;
23 _tb.FontStyle = FontStyles.Italic;
24 _tb.Foreground = Brushes.Gray;
25 _tb.Text = placeholder;
26 _tb.IsHitTestVisible = false;
27 _visCollec.Add(_tb);
28 }
29 protected override int VisualChildrenCount
30 {
31 get
32 {
33 return _visCollec.Count;
34 }
35 }
36 protected override Size ArrangeOverride(Size finalSize)
37 {
38 _tb.Arrange(new Rect(new Point(4, 2), finalSize));
39 return finalSize;
40 }
41 protected override Visual GetVisualChild(int index)
42 {
43 return _visCollec[index];
44 }
45 }
代码不难理解,就是给TextBox上面又放了一个TextBlock,并且把TextBlock的Visibility属性和TextBox的Visibility属性绑定。
最后附上文中demo下载:http://files.cnblogs.com/nanqi/NanQi.Controls.Placeholder.zip
end.
发表评论
-
C#WebBrowser控件使用教程与技巧收集--苏飞收集 - sufeinet
2013-06-28 12:07 1073原帖地址:http://www.cnblogs.com/suf ... -
我要喷一个自认为很垃圾的网站架构 - 老赵【苏州】
2013-06-28 12:01 1134原帖地址:http://www.cnblogs.com/lao ... -
[翻译] Oracle Database 12c 新特性Multitenant - Cheney Shue
2013-06-28 11:43 624原帖地址:http://www.cnblogs.com/ese ... -
memcahd 命令操作详解 - 阿正-WEB
2013-06-28 11:37 475原帖地址:http://www.cnblogs.com/azh ... -
面向过程的代码符合大众的思维方式吗? - 史蒂芬.王
2013-06-27 10:28 597原帖地址:http://www.cnblogs.com/ste ... -
面向过程的代码符合大众的思维方式吗? - 史蒂芬.王
2013-06-27 10:28 560原帖地址:http://www.cnblogs.com/ste ... -
RPG游戏之组队测试 - zthua
2013-06-27 10:22 560原帖地址:http://www.cnblogs.com/zth ... -
IT人们给个建议 - SOUTHER
2013-06-26 14:06 526原帖地址:http://www.cnblogs.com/sou ... -
Java向前引用容易出错的地方 - 银河使者
2013-06-26 14:00 497原帖地址:http://www.cnblogs.com/nok ... -
使用Func<T1, T2, TResult> 委托返回匿名对象 - 灰身
2013-06-26 13:54 801原帖地址:http://www.cnblo ... -
【web前端面试题整理03】来看一点CSS相关的吧 - 叶小钗
2013-06-25 10:45 788原帖地址:http://www.cnblogs.com/yex ... -
Windows 8 动手实验系列教程 实验6:设置和首选项 - zigzagPath
2013-06-25 10:27 624原帖地址:http://www.cnblogs.com/zig ... -
闲聊可穿戴设备 - shawn.xie
2013-06-25 10:21 568原帖地址:http://www.cnblo ... -
CentOS下Mysql安装教程 - 小学徒V
2013-06-23 15:24 612原帖地址:http://www.cnblogs.com/xia ... -
vmware安装ubuntu12.04嵌套安装xen server(实现嵌套虚拟化) - skyme
2013-06-23 15:18 840原帖地址:http://www.cnblogs.com/sky ... -
之前专门为IE6、7开发的网站如何迁移到IE10及可能遇到的问题和相应解决方案汇总 - 海之澜
2013-06-23 15:12 956原帖地址:http://www.cnblogs.com/wuz ... -
Android学习笔记--解析XML之SAX - 承香墨影
2013-06-23 15:01 413原帖地址:http://www.cnblo ... -
SQL Server 性能优化之——T-SQL TVF和标量函数
2013-06-19 09:32 676原帖地址:http://www.cnblogs.com/Boy ... -
Nginx学习笔记(二) Nginx--connection&request
2013-06-19 09:26 671原帖地址:http://www.cnblogs.com/cod ... -
从郭美美霸气侧漏看项目管理之项目经理防身术
2013-06-19 09:20 504原帖地址:http://www.cnblogs.com/had ...
相关推荐
在WinForm程序中,实现TextBox文本输入框占位符的方式也很多,最常用的是方式基于Windows Api SendMessage函数发送EM_SETCUEBANNER消息,或者通过TextBox自带的焦点事件处理。 SendMessage函数实现 创建一个继承...
WPF中,可以使用Trigger和DataTrigger来改变文本框的背景色和前景色,模拟占位符的效果。当文本框获得焦点或有内容时,颜色会变回正常,使占位符文本不可见。 4. **Validation(验证)** WPF的验证机制允许开发者...
这段代码定义了一个名为`searchTextBox`的TextBox,并设置了占位符文本“请输入搜索关键词”。`TextChanged`事件将在用户输入时触发,这是实现搜索功能的关键。 接下来,我们需要处理`TextChanged`事件。在后台代码...
在处理文本时,WPF还提供了一些附加控件和概念,如`Label`用于创建提示信息,`TextBox`和`RichTextBox`的`Watermark`效果可以实现类似占位符的功能。此外,`PasswordBox`控件用于安全地输入密码,其`Password`属性...
1. Windows Forms控件:这些是用于桌面应用程序开发的控件,如Button、Label、TextBox、ComboBox、ListBox等基本控件,以及DateTimePicker、TreeView、TabControl等更复杂的控件。新增的控件可能提供了更多的定制...
这通常涉及到自动完成、上下文相关的建议或者简单的占位符文本。本篇文章将深入探讨如何在C#中实现TextBox的智能提示功能。 1. **自动完成(AutoComplete)** C#中的TextBox控件支持自动完成功能,通过设置`...
请注意,实际应用中可能需要处理多个填空项,因此可能需要维护一个占位符列表,并根据用户操作动态地添加或移除`TextBox`控件。 通过这种方式,我们可以创建一个功能丰富的`RichTextBox`,不仅支持基本的文本编辑,...
2. **C#/.NET**:在Windows Forms或WPF应用中,可以使用Timer控件,配合TextChanging或TextChanged事件,动态更改TextBox的Text属性。对于WPF,还可以利用Storyboard和Animation来创建更复杂的动画效果。 3. **...
在这段代码中,有几个 `Label` 控件用于显示静态文本或者作为其他控件的占位符。 #### 2.3 TextBox `TextBox` 控件用于让用户输入文本。例如,`cap`、`year` 和 `eq` 这三个 `TextBox` 分别可能用于输入贷款金额、...
连续的“test test test”可能是占位符文本,也可能是开发者在不断强调测试的重要性。 【标签】"C#" 表明这个客户端是使用C#语言编写的。C#是一种由微软开发的面向对象的编程语言,广泛应用于Windows桌面应用、游戏...