深入浅出WPF
属性
- static的类型数据在内存中只有一个拷贝,非static的字段在内存中,每个对象有一个拷贝。
- 对于函数,无论是否为static,在内存中都只会有一个拷贝,区别是能够通过类名来访问存放指令的内存,还是通过实例来访问存放指令的内存。
- .Net中的CLR属性是private字段的安全访问包装,也可以说是一个private字段在后台支持一个CLR属性。
- 编译的时候,C#对属性的编译是把它编译成函数。因此,WPF中CLR属性在内存中只有一个拷贝。
- DependencyProperty必须以DependencyObject为宿主、借助它的SetValue和GetValue方法进行写入和读取。因此想使用自定义的DependencyProperty,宿主一定是DependencyObject的派生类。
- 包装器的作用是以“实例属性”的形式向外界暴露依赖属性,这样的依赖属性才能成为数据源的一个Path.
下面代码是用Slider控制图形的位置,有点意思。
<Canvas>
<Slider x:Name="sliderX" Canvas.Top="10" Canvas.Left="10" Width="260" Minimum="50" Maximum="200"/>
<Slider x:Name="sliderY" Canvas.Top="40" Canvas.Left="10" Width="260" Minimum="50" Maximum="200"/>
<Rectangle x:Name="rect" Fill="Blue" Width="30" Height="30" Canvas.Left="{Binding ElementName=sliderX, Path=Value}"
Canvas.Top="{Binding ElementName=sliderY,Path=Value}"
/>
</Canvas>
事件
- WPF中的事件,是路由事件,Routed Event。
- 事件模型的关键部分
- 事件的拥有者:myButton
- 事件:myButton.Click
- 事件的响应者:窗体本身
- 事件处理器:this.myButton_Click方法
- 订阅关系:
this.myButton.Click += new System.EventHandler(this.myButton_Click);
直接事件模型的弱点:
- 需要显示书写事件订阅代码。
- 用户控件的内部事件不能被外界所订阅,需要订阅的话,有时候需要写一个事件链。
路由事件:
- 拥有者和响应者之间没有显式的订阅关系
- 路由事件是静态字段
自定义路由事件的三个步骤:
- 声明并注册路由事件
- 为路由事件添加CLR事件包装
- 创建可以激发路由事件的方法
事件部分的代码看的云里雾里,后面再看
命令
- 如果命令与具体业务逻辑无关则使用WPF类库中的RoutedCommand类即可,如果有业务,则需要创建RoutedCommand的派生类。
资源
- ResourceDictionary可以存储任意类型的对象。
- Static资源是指在程序载入内存时对资源的一次性使用。
- Dynamic资源使用指的是在程序运行过程中,仍然会访问这些资源。
模板
- WPF的Template分为两大类:
- ControlTemplate是算法内容的表现形式,拓展控制逻辑
- DataTemplate是数据内容的表现形式
- 当为一个Binding只指定Path不指定Source时,Binding会沿着逻辑树一直向上找,查看每个节点的DataContext属性,如果DataContext引用的对象具有Path指定的属性名,Binding就会把这个对象当做自己的数据源。
- 为Template设置其应用目标有两种方法,一种是逐个设置控件的Template/ContenTemplate等属性,不想应用Template的空间不设置,另一种是整体应用,即把Template应用在某个类型的控件或数据上(这个时候要由Style,但Style不能标记x:Key)。
- 比如一个Style把x:Type TextBox类型的样式做了更改,但是有一个TextBox不想用样式,则把控件的Style标记为{x:Null}。
- 把DataTemplate应用在某个数据类型上的方法是设置DataTemplate的DataType属性,并且DataTemplate作为资源时也不能带有x:Key标记。
- Setter,设置器:
- Setter类的Property属性用来指明你想为目标的哪个属性赋值。
- Setter类的Value属性则是你提供的属性值。
- Trigger:触发器
- 事件触发器:EventTrigger
- 数据变化型触发器:Trigger/DataTrigger
- 多条件型触发器:MultiTrigger/MultiDataTrigger等 - MultiTrigger:事实上是必须多个条件同时成立才会被触发,比Trigger多了一个Conditions属性,需要同时成立的条件就存储在这个集合中。
普通的Trigger写法
<Style TargetType="CheckBox">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Trigger.Setters>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Foreground" Value="Orange"/>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
MultiSetter的写法
<Style TargetType="CheckBox">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsChecked" Value="true"/>
<Condition Property="Content" Value="如来"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Foreground" Value="Orange"/>
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
- 由数据触发的DataTrigger:基于数据进行判断
- 为了将空间自己作为数据源,需要使用RelativeSource。
- 不明确指出Source时,Binding会把控件的DataContext属性作为数据源,而不是把控件自身当做数据源。
- 工作中,常用的绘图容器是Canvas和Grid。