【WPF】创建基于模板的WPF控件(经典)
WPF可以创建两种控件,它们的名字也很容易让人混淆:用户控件(User Control)和定制控件(Customer Control),之所以如此命名,是因为用户控件更面向控件的“使用者”,以方面他们利用现成的控件组合成新的控件,而客户控件,更便于定制化(Customization),方便创建有别于现有控件的定制控件。
定制控件提供了行为和表现完全分离的开发模式,具有很高的灵活性,当然,也更难一些。这里我们通过创建个简单的搜索控件来看看如何开发定制控件:
首先我们创建一个WPF应用,在同一个solution里,再添加一个用户WPF控件库。
系统会自动在控件库里创建一个UserControl1.XAML,这个文件可以直接删除。在WPF控件库里添加一个新的项目,注意:应该选择定制控件而不是用户控件,如图:
现在程序结构看起来应该像这样子:
定制控件的模板会为我们建立FilterTextBox.cs和Generic.xaml文件。
前者内容如下:
- public class FilterTextBox : Control{
- static FilterTextBox()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(FilterTextBox),
- new FrameworkPropertyMetadata(typeof(FilterTextBox)));
- }}
generic.xaml是定制控件的外观表现,默认在themes目录下
- <Style TargetType="{x:Type local:FilterTextBox}">
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
- <Border Background="{TemplateBinding Background}"
- BorderBrush="{TemplateBinding
- BorderBrush}"
- BorderThickness="{TemplateBinding BorderThickness}">
- </Border>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- </Style>
现在generic.xaml的border还是空的。
接下来,我们先为控件创建其行为。首先我们添加一个叫"Text"的依赖属性,这是用户输入的搜索文本。这儿我们创建了个Callback,这样属性改变时就会被调用。注意不要在CLR属性的getter和setter添加任何代码,因为在运行时,WPF会忽略这些属性而直接调用GetValue和SetValue.但是在xaml里使用属性时,你仍需要CLR属性。
- public static readonly DependencyProperty TextProperty =
- DependencyProperty.Register("Text",
- typeof(String),
- typeof(FilterTextBox),
- new UIPropertyMetadata(null,
- new PropertyChangedCallback(OnTextChanged),
- new CoerceValueCallback(OnCoerceText)));
- private static object OnCoerceText(DependencyObject o, Object value)
- {
- FilterTextBox filterTextBox = o as FilterTextBox;
- if (filterTextBox != null)
- return filterTextBox.OnCoerceText((String)value);
- else
- return value;
- }
- private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
- {
- FilterTextBox filterTextBox = o as FilterTextBox;
- if (filterTextBox != null)
- filterTextBox.OnTextChanged((String)e.OldValue, (String)e.NewValue);
- }
- protected virtual String OnCoerceText(String value)
- {
- return value;
- }
- protected virtual void OnTextChanged(String oldValue, String newValue)
- {
- }
- public String Text
- {
- // IMPORTANT: To maintain parity between setting a property in XAML
- // and procedural code, do not touch the getter and setter inside
- // this dependency property!
- get
- {
- return (String)GetValue(TextProperty);
- }
- set
- {
- SetValue(TextProperty, value);
- }
- }
接着我们还要暴露出一些事件,这样当文本被修改时,控件的用户就会被通知到,这儿为控件添加一个"TextChangeEvent"。
- public static readonly RoutedEvent TextChangedEvent = EventManager.RegisterRoutedEvent("TextChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FilterTextBox));
- public event RoutedEventHandler TextChanged
- {
- add { AddHandler(TextChangedEvent, value); }
- remove { RemoveHandler(TextChangedEvent, value); }
- }
事件在OnTextChange方法里被触发:
- protected virtual void OnTextChanged(String oldValue, String newValue)
- {
- this.RaiseEvent(new RoutedEventArgs(FilterTextBox.TextChangedEvent, this));
- }
到这里,关于控件行为的代码已经基本完成了,我们继续前进,为我们的控件创建一个外观。我们添加一个DockPanel,并在其中加入一个文本框和一个按钮。按钮Dock在右边。然后我们把文本框的Text属性和我们控件的Text属性绑定起来。同时设置UpdateSourceTrigge为TextChanged,这样每次用户在文本框输入点什么东西,就会触发我们写的TextChanged事件。注意,文本框没有border,因为这儿不需要2个Border。
- <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
- <Border
- Background="{TemplateBinding Background}"
- BorderBrush="{TemplateBinding BorderBrush}"
- BorderThickness="{TemplateBinding BorderThickness}"
- CornerRadius="3">
- <DockPanel
- LastChildFill="True"
- Margin="1">
- <Button
- x:Name="PART_ClearFilterButton"
- Content="X"
- Width="20"
- ToolTip="Clear Filter"
- DockPanel.Dock="Right" />
- <TextBox
- x:Name="PART_FilterTextBox"
- Text="{Binding Path=Text,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,RelativeSource={RelativeSource TemplatedParent}}"
- BorderBrush="{x:Null}"
- BorderThickness="0"
- VerticalAlignment="Center" />
- </DockPanel>
- </Border>
- </ControlTemplate>
特别要注意的是这些控件的名称。他们都以"PART_"开头,这是WPF的标准方法用来表示那些需要被替换的控件,当我们要修改控件的模板的时候。如果有人为你的控件编写模板,你需要验证所用的部件的类型是控件所必需的。可以通过TemplatePart Attribute,并添加部件的名称和类型。
- [TemplatePart(Name = "PART_FilterTextBox", Type = typeof(TextBox))]
- [TemplatePart(Name = "PART_ClearFilterButton", Type = typeof(Button))]
- public class FilterTextBox : Control{
- ...
- }
我们还设想只有在文本框里有文本的时候,一个“清空”的按钮才显示出来。我们创建一个DataTriger来实现这个想法:
- <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
- <Border>
- ...
- </Border>
- <ControlTemplate.Triggers>
- <DataTrigger Binding="{Binding Path=Text.Length, ElementName=PART_FilterTextBox}" Value="0">
- <Setter TargetName="PART_ClearFilterButton" Property="Visibility" Value="Collapsed" />
- </DataTrigger>
- </ControlTemplate.Triggers>
- </ControlTemplate>
现在要处理的是当用户点击"清空"按钮时候的动作,可以通过重写OnApplyTemplate来实现。通过控件名调用GetTemplateChild可以得到控件的引用。当用户点击“清空”按钮时,我们想删除文本框里的所有文本。得到文本框的引用和上面的方法相同。
- public override void OnApplyTemplate()
- {
- base.OnApplyTemplate();
- Button clearFilterButton = base.GetTemplateChild("PART_ClearFilterButton") as Button;
- if (clearFilterButton != null)
- {
- clearFilterButton.Click += new RoutedEventHandler(ClearFilterButton_Click);
- }
- }
- private void ClearFilterButton_Click(Object sender, RoutedEventArgs e)
- {
- TextBox textBox = base.GetTemplateChild("PART_FilterTextBox") as TextBox;
- if (textBox != null)
- {
- textBox.Text = String.Empty;
- }
- }
现在可以在WPF应用里跑一下了!
【WPF】创建基于模板的WPF控件(经典)的更多相关文章
- WPF笔记(1.9 样式和控件模板)——Hello,WPF!
原文:WPF笔记(1.9 样式和控件模板)--Hello,WPF! 资源的另一个用途是样式设置: <Window > <Window.Resources> <St ...
- WPF关于控件 父级控件,子级控件,控件模板中的控件,等之间的相互访问
原文:WPF关于控件 父级控件,子级控件,控件模板中的控件,等之间的相互访问 1,在菜单中访问 弹出菜单的控件 var mi = sender as MenuItem;//菜单条目 MenuItem ...
- WPF备忘录(5)怎样修改模板中的控件
首先,想问大家一个问题,你们如果要给一个Button添加背景图片会怎么做?(呵呵,这个问题又点小白哈) 是这样吗? <Button Height="57" Horizonta ...
- WPF:理解ContentControl——动态添加控件和查找控件
WPF:理解ContentControl--动态添加控件和查找控件 我认为WPF的核心改变之一就是控件模型发生了重要的变化,大的方面说,现在窗口中的控件(大部分)都没有独立的Hwnd了.而且控件可以通 ...
- 【WPF学习】第二十章 内容控件
内容控件(content control)是更特殊的控件类型,它们可包含并显示一块内容.从技术角度看,内容控件时可以包含单个嵌套元素的控件.与布局容器不同的是,内容控件只能包含一个子元素,而布局容器主 ...
- WPF自定义控件与样式(9)-树控件TreeView与菜单Menu-ContextMenu
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 菜单M ...
- Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用
一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...
- Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)
jquery提供的serialize方法能够实现. $("#searchForm").serialize();但是,观察输出的信息,发现serialize()方法做的是将表单中的数 ...
- WPF中不规则窗体与WindowsFormsHost控件的兼容问题完美解决方案
首先先得瑟一下,有关WPF中不规则窗体与WindowsFormsHost控件不兼容的问题,网上给出的解决方案不能满足所有的情况,是有特定条件的,比如 WPF中不规则窗体与WebBrowser控件的兼 ...
随机推荐
- Dll注入:修改PE文件 IAT注入
PE原理就不阐述了, 这个注入是PE感染的一种,通过添加一个新节注入,会改变PE文件的大小,将原有的导入表复制到新节中,并添加自己的导入表描述符,最后将数据目录项中指向的导入表的入口指向新节. 步骤: ...
- kiwi installation
Mainly the installstion methods follow the url: https://github.com/emolch/kiwi/wiki/Installation the ...
- Java读取classpath下的文件
写Java程序时会经常从classpath下读取文件,是时候该整理一下了,并在不断深入的过程中,陆续补充上. 现在Java project 都以maven项目居多, 比如像下面这样的一个项目结构: 编 ...
- sql字段为datetime,插入''的时候默认为1900年
Microsoft SQL Server Database Engine 用两个 4 字节的整数内部存储 datetime 数据类型的值. 第一个 4 字节存储“基础日期”(即 1900 年 1 月 ...
- 移动端flex自适应方案。(px to rem)
define(function (require, exports, module) { exports.mobileUtilMethod = function () { (function (e, ...
- 轻量ORM-SqlRepoEx (三)Select语句
一.示例用数据库为Northwind数据库,可在百度网盘下载 https://pan.baidu.com/s/1er0Mm48kUfeAsYkSW6DfnA 密码:r7pm 二.如何初始化SqlRep ...
- js日期相减得到分钟数
const date1 = new Date(fieldsValue.examStartTime); const date2 = new Date(fieldsValue.examEndTime); ...
- Javascript中的this对象
对于this的使用,我们最常遇到的主要有,在全局函数中,在对象方法中,call和apply时,闭包中,箭头函数中以及class中: 我们知道this对象是在运行时基于函数的执行环境绑定的,在调用函数之 ...
- #leetcode刷题之路1-两数之和
给定两个整数,被除数 dividend 和除数 divisor.将两数相除,要求不使用乘法.除法和 mod 运算符.返回被除数 dividend 除以除数 divisor 得到的商. 示例 1:输入: ...
- ABAP术语-IDOC
IDOC 原文:http://www.cnblogs.com/qiangsheng/archive/2008/02/21/1075988.html Intermediate Document Inte ...