WPF 学习笔记
XAML
XAML 文档
- 在运行期被编译成 BAML。
- 元素对应于 .NET 类。
- 一个元素可以嵌在另一个元素中。
- 可以使用属性。
XAML 名字空间
- XAML 名字空间使用属性来定义。
- XAML 名字空间通常定义在顶层元素中。
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
缺省名字空间 - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
依赖属性(Dependency Property)
相比较于普通属性,依赖属性有以下特点:
- 变化通知(change notification)
- 节省内存
- 使用某种带优先级决定策略(resolution strategy)来读取属性的值。
属性值可以依赖于其他对象,适用于数据绑定,属性值继承等场景。
依赖属性的声明
- 宿主必须是 DependencyObject 的子类。
- 使用 DependencyProperty 类型声明依赖属性。(public static readonly)
- 使用 DependencyProperty.Register 方法注册依赖属性。
- 使用属性包装器包装依赖属性(调用 GetValue, SetValue)。
附加属性(Attached Property)
附加属性是所处环境(上级元素)所附加的属性,是一种特殊的依赖属性。
- 宿主必须是 DependencyObject 的子类。
- 使用 DependencyProperty 类型声明附加属性。(public static readonly)
- 使用 DependencyProperty.RegisterAttached 方法注册附加属性。
- 使用静态方法 GetXXX, SetXXX 存取附加属性(调用 GetValue, SetValue)。
路由事件(Routed Events)
路由事件被用来实现事件的路由功能。
路由事件的声明
- 宿主必须是 FrameworkElement 的子类。
- 使用 RoutedEvent 类型声明路由事件。(public static readonly)
- 使用 EventManager.RegisterRoutedEvent 方法注册路由事件。
- 使用标准 .NET 事件声明机制包装路由事件(add, remove)。
路由策略
- 直接:事件由事件源直接触发。
- 冒泡:由事件源逐级向上传递直至根元素。
- 隧道:由根元素逐级向下传递直至事件源。
隧道事件名通常以 Preview 打头。
附加事件(Attached Event)
由下级元素的冒泡事件附加的事件。
布局(Layout)
不应该用硬编码来指定控件的尺寸和位置。
常用面板容器
- StackPanel
纵向或横向布局,只有一列或一行 - WrapPanel
纵向或横向布局,可以不止一列或一行,空间不够时可换列或换行 - DockPanel
东西南北中的布局模式 - Grid
将指定区域划分为若干行和列 - Canvas
使用绝对位置来定位控件
布局属性
- HorizontalAlignment:Center, Left, Right, or Stretch
水平方向可以选择居中,左对齐,右对齐或延伸。 - VerticalAlignment:Center, Top, Bottom, or Stretch
垂直方向可以选择居中,左对齐,右对齐或延伸。 - Margin:外边距,可以选择一个数字或四个数字(左上右下)
- MinWidth MinHeight:
- MaxWidth MaxHeight:
- Width Height:
Border(边框)
- Background:背景色
- BorderBrush BorderThickness:
- CornerRadius:
- Padding:内边距
资源(Resources)
使用资源标签将有用的对象聚合于一处,便于修改和复用。即使用资源是为了在应用程序的不同部分共享某些对象。
在 XAML 中定义资源相当于在程序代码中定义常量。
- 资源在资源(Resources)标签中定义。使用字符串作为键值(x:Key=)。
- 通常在顶层的 Window (级别)标签中定义。
静态资源和动态资源
- 静态资源只提取一次(预先提取)
- 动态资源可提取多次(只在需要时提取),资源对象在代码中被替换时也能及时响应
- 通常不需要动态资源
资源的使用
- 非共享资源(Nonshared Resources):使用 x:Shared="False" 创建独立的资源对象,通常不需要
- 在代码中可以调用 FindResource 方法定位资源
- 应用程序级别的资源:
- 系统资源(System Resources):
资源字典(Resource Dictionary)
在不同的工程中共享资源需要使用资源字典,资源字典通常用于定义应用程序的皮肤(Skin)。
- 使用单独的文件定义资源字典
- 在资源标签下使用 ResourceDictionary 标签将资源字典并入程序中
- 额外的资源此时只能在 ResourceDictionary 标签下定义
样式(Style)
样式是一组(属性及事件)设置的集合。样式在资源中定义。
样式的构成
- Setters:
- Setter:属性设置
- EventSetter:事件设置
- Triggers:条件满足时自动触发对某些设置,条件不满足时自动还原
- Trigger:观察控件的某个依赖属性,属性值为某个特定值时触发
- MultiTrigger:多个 Trigger 同时满足时触发
- DataTrigger:观察控件所绑定的数据对象的某个属性,属性值为某个特定值时触发
- MultiDataTrigger:多个 DataTrigger 同时满足时触发
- EventTrigger:控件的某个特定事件发生时触发
- Resources:局部于样式的资源
- BasedOn:样式基于某个类型
- TargetType:目标类型,用于自动应用样式
数据绑定(Data Binding)
通过在目标对象的目标属性中设定绑定源及其值路径(属性名 Path=)来实现数据绑定。
绑定其他界面元素
- 指定界面元素名作为绑定源。(ElementName=)
- 绑定模式(方向 Mode=)
- OneWay:单向,源属性到目标属性
- TwoWay:双向,源属性到目标属性,目标属性到源属性
- OneTime:单向,源属性到目标属性,仅限一次
- OneWayToSource:单向,目标属性到源属性
- Default:由目标对象元数据决定
- 方向为目标属性到源属性时触发源属性更新的因素(UpdateSourceTrigger=)
- PropertyChanged:目标属性变更时
- LostFocus:目标属性变更后且失去焦点时
- Explicit:明确调用 UpdateSource 方法时
- Default:由源属性元数据决定
绑定数据源
指定资源或对象作为绑定源。(Source=)
绑定相对数据源
寻找某个与自身存在某种相对位置关系的元素,把它作为绑定源。(RelativeSource=)
- 绑定模式(目标 Mode=)
- Self:自身
- FindAncestor:上级元素
- PreviousData
- TemplatedParent
- 绑定模式为上级元素时需要满足的条件
- 指定上级元素的种类(AncestorType=)
- 找到第n个指定种类的上级元素(AncestorLevel=)
绑定数据上下文
没有指定数据源时,逐级向上寻找上级元素的 DataContext 属性,该属性非空时把它作为绑定源。
绑定对象的集合
使用 ObservableCollection
数据转换(Data Conversion)
- 字符串格式化(StringFormat=)
- 创建值转换器(Value Converters)
- 实现 IValueConverter 接口
- 在 Convert 方法中将源对象转换为目标对象
- 在 ConvertBack 方法中将目标对象转换为源对象
- 使用值转换器
- 引入值转换器所在的名字空间
- 在资源中创建值转换器的实例
- 绑定值转换器的实例(Converter=)
多重绑定
使用 MultiBinding 标签绑定多个源。
在多重绑定标签里也可以使用
- 字符串格式化
- 值转换器(实现 IMultiValueConverter 接口)
命令(Command)
命令是程序所执行的某些功能(任务)抽象后的产物。
通过命令可以
- 将执行这些任务时的业务逻辑集中于一处
- 管理这些任务的可用性
命令模型
- ICommand 接口
- Execute 方法
- CanExecute 方法
- CanExecuteChanged 事件
- RoutedCommand 类
- 实现了 ICommand 接口
- Name 属性
- RoutedUICommand 类
- RoutedCommand 类的子类
- Text 属性
执行命令
命令的绑定(Command=)
- 命令事件未响应时,命令不可用,所绑定的控件也不可用
- Executed 事件
- CanExecute 事件
命令的执行
- 调用指定命令对象的 Execute 方法
- 调用 CommandBindings 集合中某个元素的 Execute 方法
数据验证(Validation)
INotifyDataErrorInfo 接口
要对数据对象进行数据验证,数据对象必须实现 INotifyDataErrorInfo 接口,该接口包含以下三个成员。
- ErrorsChanged 事件(错误增加或减少时)
- HasErrors 属性(数据对象是否有错误)
- GetErrors 方法(返回所有错误的信息)
自定义验证规则(Custom Validation Rules)
- 通过继承 ValidationRule 类并重写 Validate 方法可以实现自定义验证规则
- 自定义验证规则可以加入到控件的 Binding.ValidationRules 集合中以实现对单一控件的数据验证
- 自定义验证规则可以加入到容器的 BindingGroup.ValidationRules 集合中以实现对多个控件的集体验证
- 验证规则在数据转换之前执行,只有验证规则通过后才进行数据转换
- 验证规则可以有多条,依照先后顺序来执行
提示验证错误
- 当控件的 Binding.NotifyOnValidationError 属性为 true 时,控件验证出错时会触发 Error 事件。
- 控件的 ValidationError.ErrorContent 属性包含了错误信息。
- 控件的 Validation.HasError 属性提示该控件是否出错
- 控件的 Validation.ErrorTemplate 属性定义了出错时的提示性界面,覆盖该属性可实现自定义提示性界面。
自定义控件(UserControl 子类)
- 自定义控件(UserControl 子类)向外界暴露的可绑定的属性必须是依赖属性
- 自定义控件(UserControl 子类)所定义的路由事件必须是依赖属性
- 顶层的 UserControl 标签中不应该定义 Name 属性,否则该属性将被暴露给控件的使用者
- 顶层的 UserControl 标签中不应该使用 DataContext 属性,否则该控件的使用者所定义的 DataContext 属性将被覆盖
- 为了方便自定义控件(UserControl 子类)自身子控件的绑定,可以在 UserControl 标签的直接下级子标签(比如 Grid)中使用 DataContext 属性
WPF Binding to UserControl´s DependencyProperty not working as expected [duplicate]
WPF UserControl Binding Problem