《深入浅出WPF》读书笔记

来源:互联网 发布:mac用的播放器 编辑:程序博客网 时间:2024/06/08 12:17

XAML
控件
Binding (Path, Source)
属性(依赖属性)
事件(路由事件)
命令(命令4要素)
资源(Resources属性+resx)
模板(DataTemplate + ControlTemplate)
绘图 (Path)
动画 (DoubleAnimation)

核心:WPF – 数据驱动
XAML
XAML派生于XML
命名空间定义: xmlns[:可选的映射前缀]=“名称空间”。没有命名前缀的名称空间称为默认名称空间。默认名称空间有且只有一个。
xmlns:n= "http://schemas.microsoft.com/winfx/2006/xaml/presentation" –对应的是与绘制UI相关的程序集
xmlns:x= "http://schemas.microsoft.com/winfx/2006/xaml"—对应XAML语言解析处理相关的程序集,用来引导XAML编译器把XAML代码编译成CLR代码。
XAML解析成的类和后置代码中的类都使用partial,所以两个类会合二为一。
XAML注释: <!—XXXX -->
XAML为对象属性赋值的两种方法:
1. Attribute =”value”, 比如 Height=”200”
2. 使用属性元素进行复杂赋值
<Rectangle.Fill>
    <SolidColorBrush Color=”Blue”/>
</Rectangle.Fill>
标记扩展:Attribute = “{XXX}”
紧邻左花括号的是要赋给Attribute的对象,其后是该对象的属性。
比如:Text=”{Binding ElementName=slider1, Path=Value, Mode=OneWay}”
事件属性: Click=”button1_Click” ---编译器会在后置代码中自动生成该处理函数
代码后置:在XAML里面设定UI,指定事件处理函数,在C#里面实现事件处理逻辑。
代码后置文件一般为~.xaml.cs


XAML中引用程序集
1. 把类库引用到项目中
2. 在XAML中引用名称空间:
Xmlns:映射名=“clr-namespace:类库中名称空间的名字;assembly=类库文件名”(XAML编译器会有提示)
3. 使用该命名空间中的类:
<映射名:类名>…</映射名:类名>
xmlns:x= "http://schemas.microsoft.com/winfx/2006/xaml"
x命名空间中包含的类均与解析XAML语言相关
x:Class告诉XAML编译器将XAML内容与后置代码中指定的类合并
x:ClassModifier设定标签生成类的访问控制级别- internal, private, protected and public
x:Name为对象生成一个引用变量,该变量能在后置代码里面使用。如果该对象有Name属性,则会把值设定过去。
X:FieldModifier改变引用变量的访问级别(必须与x:Name合用)。WPF里面成员变量默认是internal, C#里面默认是private.
X:Key为Resources贴上用于检索的标签索引。然后在XAML或后置代码中可以使用该Key定义的资源。
 XAML:
  <Window.Resources>
      <sys:String x:Key=”mystring”>Hello World!</sys:String>
</Window.Resources>
  <TextBox Text=”{StaticResoure ResourceKey=mystring}”/>

Code:
  String str = this.FindResource(“mystring” ) as string;

注:
1. XAML中使用String类 需要 xmlns:sys=”clr-namespace:System;assembly=mscorlib” 引用mscorlib.dll
2. 引用本程序集名称空间需要 xmlns:local=”clr-namespace:XXX”
x:Shared 需要与x:Key合用, x:Shared为true, 则每次检索到这个对象时,得到的都是同一个对象;为false,则每次检索到这个对象时,得到的都是这个对象的一个新副本。默认为true.
x:Type 标记扩展, 代表一个数据类型,也就是在后置代码中用Type定义一个变量,在XAML中用标记扩展指定Type类型。
x:Null 标记扩展,XAML里表示空值的是x:Null,可以为一个属性赋空值,主要是用于Style.
x:Array标记扩展,通过它的Items属性向使用者暴露一个类型已知的ArrayList实例。ArrayList内成员的类型由x:Array的Type指明。
x:Static标记扩展,可以在XAML里面使用后置代码中的static属性字段。
X:Code作用是可以包含一些本应该放置在后置代码中的c#代码。不要这么用。


控件与布局
WPF的UI是树形结构。
Logic Tree: 宏观的,由控件组成的树。
Visual Tree: 微观的,包含控件本身的微观树在内的树。
ContentControl族:
 


HeaderedContentControl族:
 
 
ItemsControl族:
 
 
WPF布局元素:
 
Grid行高 列宽三种设置方法:
绝对值:“25px”。( 默认单位省略单位像素)
比例值:“4*”(解析器会把所有比例值的数值加起来作为分母、把每个比例值的数值作为分子,再用这个分数值乘以未被占用空间的像素,得到的结果就是分配给这个比例值的最终像素)。
自动值: “Auto”
把控件添加到Grid里面规则(附加属性):
 
比如:
 
 

Binding
Binding的源Source, 源中的哪个属性Path
一般绑定的源是逻辑层的对象,而目标是UI层的控件
绑定到类的属性上:
1. 定义类派生于INotifyPropertyChanged接口,定义PropertyChangedEventHandler事件, 当绑定的属性发生变化时(set)触发该事件:
 
 
2. 使用Binding把类和目标控件绑定到一起:
Student stu = new Student();
Binding binding = new Binding(“Name”);
Binding.Source=stu;
textBox1.SetBinding(TextBox.TextProperty,binding);
控件作为Binding的源(使用ElementName):
<TextBox  x:Name=”textBox1” Text=”{Binding Path=Value, ElementName=slider1}”/>
Binding的重要属性:
Mode:控制绑定的方向。BindingMode类型,可取值TwoWay, OneWay, OnTime, OneWayToSource和Default(根据实际情况而定).
UpdateSourceTrigger:控制何时更新source. UpdateSourceTrigger类型。可取值PropertyChanged, LostFocus, Explicit和Default.
NotifyOnSourceUpdated – 监听source改变。bool类型。
NotifyOnTargetUpdated – 监听Target改变。bool类型。
Binding的Path:
1. 绑定到source的属性上: Path=属性名。比如<TextBox x:Name=”textBox1” Text=”{Binding Path=Value, ElementName=silider1}”/>
2. 多级路径:点下去。比如:<TextBox x:Name=”textBox1” Text=”{Binding Path=Text.Length, ElementName=textBox2}”/>
3. 索引器:比如:<TextBox x:Name=”textBox1” Text=”{Binding Path=Text[3], ElementName=textBox2}”/> //绑定到Text的第4个字符。
4. 集合的默认元素:
 
5. 集合的子集合:
 
6. 数据源本身是数据,比如int ,string,则Path = .或者没有Path
Binding的Source
1. 普通类作为Source,即把类实例赋给Source,该类需派生自INotifyPropertyChanged接口,在属性的set里激发PropertyChanged事件来通知Binding数据已被更新。
2. 集合作为Source,一般是把控件的ItemsSource属性使用Binding关联到集合对象上。
3. ADO.NET数据对象作为Source.
4. 使用XmlDataProvider把XML数据指定为Source.
5. 把DataContext指定为Source(WPF默认绑定行为)。只指定Path,而不指定Source, Binding会自动把DataContext当做Source, 会沿着树向树根查找,直到找到带有Path指定属性的对象为止。
6. 通过ElementName指定Source. 用在XAML中,指定另一个控件为Source.
7. 通过RelativeSource属性相对地指定Source.用于控件关注自己的,自己容器的或者自己内部元素的某个值。
8. 把ObjectDataProvider指定为Source:当数据源的数据不是通过属性而是通过方法暴露给外界的时候。
9. LINQ检索的数据对象作为Binding的数据源。
DataContext做为数据源 – 只指定Path,没指定Source
在UI树上的每一个结点都有DataContext属性(依赖属性),当一个Binding只知道Path而不知道Source时候,它会沿着UI树一直向树根的方向上寻找,每路过一个结点就会看看这个结点的DataContext是否具有Path所指定的属性。如果有,那么就把这个对象作为Source;如果没有就会继续找下去。
 
 
将集合元素指定为Item Source
例子:把List<T>绑定到ListBox并指定显示的属性。
 

如果想显示List<T>中对象的全部属性,则需删除this.listBoxStudents.DisplayMemberPath,并定义DataTemplate.
 
XML作为绑定的Source
例子
 

 
 
 
 
注:
1. Xml作为绑定的数据源时,使用的是XPath,而不是Path.
2.XAML中, XPath=@Id, 和XPath=Name,使用@号表示的是XML元素的Attribute,而不加@的为子集元素。
使用ObjectDataProvider绑定到函数返回值
ObjectDataProvider – Wraps and creates an object that you can use as a binding source.
ObjectDataProvider对象成员:
ObjectInstance-指定具体作用的类实例
MethodName-指定类中的要调用的方法
MethodParameters – 指定方法的参数
Data-函数返回值
例子:
 

使用RelativeSource: 通过数据源的类型和UI布局上的相对关系来查找源。也可以绑定到自己。
RelativeSource成员
AncestorLevel – 以绑定目标控件为起点的层级偏移量。
AncestorType – 告诉Binding寻找哪个类型的对象作为自己的源。
Mode – 是RelativeSourceMode枚举,它的取值有PreviousData, TemplatedParent, Self和FindAncestor.
例子:
 
Binding的数据校验
实现派生于ValidationRule的类,实现Validate()方法,将该派生类的实例添加到Binding对象的ValidationRules属性。
 
 
  注:
1. 默认校验的都是验证Target传给source的数据。而认为source中的数据都是对的。如果想要对source中的数据进行验证,则需将派生类(派生于ValidationRule)的ValidatesOnTargetUpdated属性设定为true.
2. 处理数据校验时,ValidattionRule的Validate()方法的返回值ValidationResult类型会以路由事件的形式,在以Binding对象的Target为起点的UI元素树上传播。前提是Binding的NotifyOnValidationError设定为true.
 
 
Binding的数据转换
即定义个类实现IValueConverter接口的Convert()和ConvertBack()方法,然后把实例赋给Binding的Convert属性。
MultiBinding
即绑定到多个数据源
MultiBinding有个Bindings属性,通过这个属性把一组Binding对象聚合起来,处在这个集合中的Binding对象可以拥有自己的数据校验和转换机制,他们汇集起来的数据将共同决定传往MultiBinding目标的数据。
例子:
 

属性
属性并不会比定义字段多占内存,仅是两个方法的内存。因为属性在内存中是以方法存的,所以多个实例也只是两个方法的内存量。(通过IL反汇编器查看)
依赖属性:自己没有值,能通过使用Binding从数据源获得值的属性。拥有依赖属性的对象叫依赖对象。依赖属性可以节省实例对内存的开销(定义时使用static)。
传统的.NET开发中,一个对象所占用的内存空间在调用new进行进行实例化时候就已经定了,而WPF允许对象在被创建时候不包含用于存储数据的空间,只是保留需要用到数据时候能够获得默认值,借用其他对象数据或实时分配空间的能力。
WPF中所有的UI控件都是依赖对象。但不是所有属性都是依赖属性。
依赖属性,依赖对象的定义:
 
1. 依赖对象必须派生于DependencyObject.
2. 修饰符用public static readonly DependencyProperty XXX = DependencyProperty.Register(……).
依赖属性的CLR属性包装:
DependencyObject的两个方法SetValue(Student.NameProperty,XXX) 和GetValue(Student.NameProperty)用于设定和获得依赖属性的值。
但是一般都会把依赖属性进行CLR属性包装,这样可以跟CLR属性一样使用:
 
 

输入propdp就会半自动地创建依赖属性,并用CLR属性封装。
依赖属性本质:定义依赖属性的public static readonly 实际上是定义一个全局索引,而真正的依赖属性是存在每个类实例的数组里,如果没用过该依赖属性则数组里就没存。
附加属性:把对象放入一个特定的环境后才具有的属性。本质是依赖属性。比如TextBox放到Grid里面就有Grid.Column, Grid.Row等属性。
 
输入propa就会半自动地创建附加属性,并用CLR属性封装。
附加属性的定义:
 
1. 附加属性实在环境里定义的,然后会赋给环境中的对象。
 
2. 附加属性定义使用DependencyProperty.RegisterAttached()
3. 附加属性是使用两个方法进行封装的,而不是CLR属性。

事件
路由事件:
沿着visual tree单一方向传播。事件的拥有者只负责激发事件,事件的响应者则安装有事件侦听器,针对某事件进行侦听,当有此类事件传递至此时,事件响应者就使用事件处理器来响应事件并决定事件是否可以继续传递下去。
事件的传递策略可以是从触发事件控件向根传递,也可以从根节点向触发事件控件传递,还可以直达式,即直接送给事件处理器。
路由事件的侦听
WPF系统中的大多数事件都是可路由事件。
1. 后置代码中使用AddHandler:
This.gridRoot.AddHandler(Button.ClickEvent,
new RoutedEventHandler(this.ButtonClicked));
2. XAML中: Button.Click=”ButtonClicked”
<Grid x:Name=”gridRoot” Button.Click=”ButtonClicked”>
</Grid>

删除路由事件用RemoveHandler().
自定义路由事件
1. 声明并注册路由事件
Public static readonly RoutedEvent ClickEvent=EventManager.RegisterRoutedEvent(“Click”, RoutingStrategy.Bubble, typeof(RountedEventHandler), typeof(ButtonBase));
第一个参数:路由事件的名称。
第二个参数:指定路由事件的策略。
Bubble –冒泡式, 从激发者向树根传递。
Tunnel – 隧道式,从树根向激发者传递。
Direct –直达式,直接将事件消息送达事件处理器。
第三个参数:指定事件处理器的类型。
第四个参数:指定路由事件的宿主类型。
2. 为路由事件添加CLR事件包装
这样路由事件就可以和普通事件一样使用+= , -=
Public event RoutedEventHandler Click
{
add { this.AddHandler(ClickEvent, value);}
remove {this.RemoveHandler(ClickEvent,value);}
}
3. 创建可以激发路由事件的方法
new 一个RoutedEventArgs 或者其派生类, 完后调用RaiseEvent().

 
终止路由事件的传播:
在事件处理函数中将e.Handled=true.(e为RoutedEventArgs或其派生类)
RoutedEventArgs的
Source – 路由事件在Logix Tree上的源头
OriginalSource – 路由事件在visual tree上的源头
附加事件
就是路由控件,只是其宿主本身不具备显示在界面上的能力,必须附加到控件上。比如KeyDown事件,MouseEnter事件。
附加事件例子:
 
 
少用附加事件,多用Binding.


命令
命令
命令是一种机制,它从命令源发送给命令目标,在发之前可以检测是否可以发,以及送达以后执行什么操作。
命令四要素:
命令(Command):即实现ICommand接口的类,平时使用最多的是RoutedCommand类,还可以自定义命令。
命令源(Command Source):即命令的发送者。
命令目标(Command Target):即命令发送给谁,或者说命令作用在谁身上。
命令关联(Command Binding):负责把一些外围逻辑与命令关联起来。比如执行前对命令是否可以执行进行判断,命令执行后还有哪些后续操作等。
注:命令关联(Command Binding)一定要设定在外围控件上!
命令与事件区别在于事件不具备约束力,而命令具有约束力。
使用命令会有效率问题,因为命令目标发送PreviewCanExecute, CanExecute, PreviewExecuted和Executed事件沿着树向外围发送,被命令关联捕获。这个发送的频率较高,所有有效率问题。
使用命令步骤:
1. 声明并定义命令。
2. 将命令赋给命令源的Command属性
3. 指定命令目标,即指定命令源的CommandTarget属性。如果没指定命令目标,则当前拥有焦点的对象就是命令目标。
4. 创建命令关联,并将命令关联安置在外围控件上。
例子:
 
 
WPF的命令库:
 
如果程序中需要Open, Save, Play, Stop等标准命令,那就没必要自己声明了,直接用库就行。
命令里携带参数
即指定命令源时,通过设定命令源的CommandParameter来指定命令参数。在命令的4个事件处理函数中,通过参数的Parameter属性来获得参数的值。
 
自定义命令:
1. 自定义新的RoutedCommand实例(如果WPF库中没有)。
2. 从0开始命令系统。

资源
WPF中的资源分两种:每个元素的Resources属性(所有的WPF界面元素都有自己的资源-Resources属性);二进制资源。
元素的Reources属性资源
1. 定义:使用x:Key
 

2. XAML里使用:使用StaticResource/DynamicResource标记扩展
<TextBlock Text=”{StaticResource str}”/>
注:StaticResource和DynamicResource用法一样,只是前者不检查更新,后者有更新则改变。
3. 后置代码中使用:使用FindResource()
String strTxt = (string)this.FindResource(“str”);

注:查找资源是沿着树向树根查找,所以注意查找的起始位置。
Resources.resx里定义的资源
1. XAML里使用:
字符串:
Xmlns:prop=”clr-namespace:WpfApplication1.Properties”
<TextBlock Text=”{x:Static prop:Resources.UserName}”>
图片:
<Image x:Name=”ImageBg” Source=”Resources/Images/Rafale.jpg”/>
(也可在Image的Properties编辑器里直接设定Source来实现)
2. 后置代码里使用:
字符串:
This.textBlockPassword.Text=Properties.Resources.Password;
图片:
 


模板
ControlTemplate – 对已有控件的改造。是控件的外衣
DataTemplate – 数据显示。是数据的外衣
DataTemplate
常用的地方有三处:
1. ContentControl的ContentTemplate属性,相当于给ContentControl穿上外衣。
 
2. ItemsControl的ItemTemplate属性,相当于给ItemsControl的数据条目穿衣服。
 
3. GridViewColumn的CellTemplate属性,相当于给GridViewColumn单元格里的数据穿衣服。


DataTemplate的使用步骤
1. 定义DataTemplate,并将其指定给上述中的ContentTemplate、ItemTemplate和CellTemplate属性。这个可以直接在该属性里直接定义,也可以在Resources里定义,完后使用标记扩展赋给该属性。
 
2. 填充数据源
使用Blend来查看已有控件的内部结构,只有了解控件内部结构才能设计ControlTemplate.
ControlTemplate创建
ControlTemplate是在Blend里面创建的。(vs2013也可以,只是无法剖析内部)
创建编辑ControlTemplate, 右键控件->Edit Template ->Edit a copy(从当前控件改) 或者Create Empty(从头开始写)。
 
最后会在XAML里生成style.
1. 指定Key,则生成的Style包含key, Apply to all则不指定key.
2. ControlTemplate作为资源可以放在三个地方:Application的资源词典里,某个界面元素的资源词典里或者放在外部XAML文件中。
把带key的style里面的ControlTemplate应用到某个控件上,即通过标记扩展把它赋给控件的Style属性
 
把ControlTemplate设定为指定控件的默认形式
即定义Style时候不指定x:Key, 如果某个控件不想用则指定该控件Style=”{x:Null}”
 
把DataTemplate设定为默认形式
 
访问控件内部用VisualTreeHelper
Style设定控件的默认属性(Setter)和行为(Trigger)
Style中的Setter
Setter类有两个属性:Property(目标属性)和Value(属性值)
例子
 
 
Style中的Trigger – 满足条件,触发行为
Trigger类三个属性:Property(关注的属性), Value(触发条件)和Setters(触发时赋值,不满足触发条件时还原)
 
 
 
Style中的MultiTrigger – 同时满足多个条件才触发行为
条件存在MultiTrigger.Conditions结点下,而行为存在MultiTrigger.Setters节点下
 
Style中的DataTrigger – 由控件的数据(比如Text.Length)而不是属性触发的行为。
 
 
MultiDataTrigger – 同时满足多个数据条件触发的行为
EventTrigger – 控件事件触发动画。

绘图和动画
WPF基本图形
 
直线 Line 属性
1. X1 Y1 X2 Y2 – 设定起点终点
2. Stroke – 颜色
3. StrokeThickness – 宽度
4. StrokeDashArray – 设定虚线
5. StrokeEndLineCap – 终点形状
矩形 Rectangle
矩形由笔触(Stroke,即边线)和填充(Fill)构成。Strike属性设置与Line一样,Fill属性数据类型是Brush.
 
Path 路径
Path完全可以替代其他图形,而且他可以将直线,圆弧,贝塞尔曲线等基本元素结合起来,画出更复杂的图形。
Path的Data属性即包含拼接的图形,其类型为Geometry.
Data的属性语法有两种:标签式和路径标记法。
一般用Geometry的子类给Data赋值:
 
Path的Data属性标签式赋值:
 
PathGeometry用于Path中画首尾相接的复杂的几何图形
PathGeometry里面用的各种线段:
 
所有这些线段都是没有起点,因为起点就是前一个线段的终点,而第一个线段的起点则是PathFigure的StartPoint.
 
 
Path的路径标记语法
即用字母表示画什么,用x,y坐标点来标记画到哪
分三步:移动至起点-》绘图-》闭合图形
 
Blend中的Combine
同时选中多个图形(控件不行),然后右键Combine
。Unite: 相并
。Divide: 拆分
。Intersect:相交
。Subtract: 相减
。Exclude Overlap: 排除重叠。
Effect属性为UI添加效果:
 
也可以从效果库里面添加效果, p/265
图形变形
WPF中的变形和UI元素是分开的,变形本身是一个Object.
控件变形的两个依赖属性:
RenderTransform:呈现变形,定义在UIElement类中。
LayoutTransform:布局变形,定义在FrameworkElement类中。
它俩的数据类型都是Transform抽象类。
 
 
WPF规定, 可以用来制作动画的属性必须是依赖属性
WPF中动画分类:
AnimationTimeline:简单动画,由一个元素完成。
StoryBoard:复杂动画, 需要UI上的多个元素协同完成。
派生于AnimationTimeline的简单动画类:
 
具体使用哪个类取决于要根据哪个依赖属性来做成动画,该属性的类型。大部分依赖属性都是double,所以大部分都用DoubleAnimation.
简单线性动画4要素:(注DoubleAnimation类都有这4属性)
1. 变化时间(Duration属性):动画经历的时间。必须指定,数据类型为Duration.
2. 变化终点(To属性):动画终点。如果没有指定动画终点,程序将采用上一次动画的终点或默认值。
3. 变化幅度(By属性):动画幅度。如果同时指定了变化终点,变化幅度将被忽略。
4. 变化起点(From属性):动画起点。如果没有指定变化起点则以变化目标属性的当前值为起点。
简单线性动画的步骤:
1. 定义控件的TranslateTransform(动画是作用在控件的TranslateTransform上的)。
2. 定义简单线性动画类,指定4要素。
3. 调用目标控件的TranslateTransform对象的BeginAnimation()方法开始动画。
例子:
 
 
高级动画特效控制
(以下都是DoubleAnimation等动画类的属性)
 
 
例子:(BounceEase至少为Framework4)
 
关键帧动画(使用DoubleAnimationUsingKeyFrames而不是DoubleAnimation)
即动画执行到哪个时间点,被动画所控制的属性值也必须达到设定的值。
路径动画(使用DoubleAnimationUsingPath)
即沿着指定路径动画。
比如: