我不知道的WPF
1、当没有显式的指定窗口的大小时,窗口的高和宽都是NaN,因此不可以使用Width和Height,而应该使用ActualWidth和ActualHeight ,来获取当前窗口的实际尺寸。不过以上两个属性是只读的,而且只有在窗口被绘制以后才生效。
2、WPF的度量时使用设备无关单位。为任何控件设置的长度值,都不是像素值(它们可以被设置为double),而是一个设备无关值。这里的单位是 1/96英寸,这与windows的默认显式分辨率一致(每英寸96像素),以致于看上去我们是设定了一个像素值。这样做的好处是,即使显示设备的精度会 越来越高,但WPF绘制出的布局却不会因此而变小,它们依然会保持原有的大小。
3、Brush画刷类都是从Freezable 抽象类继承过来的。Freezable 的意思是对象可以被设置为frozen状态,frozen状态的意思是不能再被改变。对象的IsFrozen Property一旦变成true,就表示对象已经被冻结,冻结对象可以提高效率,因为被冻结的对象不再需要监控。下面是一个冻结的例子:
通过new SolidColorBrush 和 使用 Brushes.Red赋值得到的画刷是有区别的,后者在生成的时候已经被冻结了,所以对后者重新赋值的时候会出现Invalid Operation Exception。
解冻操作是不存在的,但是可以通过Clone操作,获取一个没冻结的复制版本。
Freezable对象只能被创建它的线程所改变。
4、BorderBrush会使客户区的面积变小。
5、径向渐变画刷的几个扩散选项的效果写法。
brush.SpreadMethod = GradientSpreadMethod.Reflect;
brush.SpreadMethod = GradientSpreadMethod.Repeat;
6、Window类的Content property 是从contentControl继承的。又被定义为object,因此可以定义为任何的东西(除了window)。不过只能设定一个内容给content。想要添加多个内容,就要使用布局控件了。
7、在Content property的世界中,,分成两组对象,一组继承UIElement,一组不是。后者会使用toString来显示,前者将使用OnRender来显 示。假如一个控件不是contentControl,又被设定为Content的内容,那么就会调用控件的toString方法进行显示。若 Content property被设置成一个字符串,那么ContentControl将会先创建一个textBlock对象,然后再实际显示此字符串。
8、早期windowsAPI中,几乎屏幕上所有的东西,都被认为是控件,现在却不是这样。控件是视觉对象(Visual Object),其特征是对用户的输入有反应。
9、TextBlock中各个文本的格式可以不一致。TextBlock有一个inlines属性,是Inline对象的collection。继承自FrameworkContentElement。 collection可以加入字符串对象,Inline对象和UIElement对象。Inline对象包括:Run Span (Bold Hyperlink Italic Underline)
text.Inlines.Add("this is a ");
text.Inlines.Add(new Bold(new Run("TextBlock")));
text.Inlines.Add(new Italic(new Run("Demo")));
Content = text;
10、UIElement 与子类 FrameworkElement 与 ContentElement 和子类 FrameworkContentElement的区别
ContentElement没有OnRender方法,它们不会在屏幕上把自己画出来,要通过“继承自UIElement的类”的OnRender方法 画出来。理解一下上面的代码,Bold或Italic构造函数只接收Inline对象,不接受字符串对象,所以需要调用Bold和Italic的Run构 造函数。
11、屏幕上的element会根据逻辑树组织,从父亲向下传递属性值。除非孩子显式地设定某些Property,否则将直接沿用父亲的Property值。例如窗口的Foreground会传递到下面的各个控件。
12、ButtonBase定义了一个名为ClickMode的Property,指定按钮要如何反馈点击,即什么时候下产生Click事件。通常都是release,但是Press和Hover都有需要他们的场合。
13、关于Margin和padding。margin是外边距,padding是内边距。在WPF中,应该少用Width和Height属性,而通过margin属性去获得自动调整的能力。
14、Resource的读取。程序中可以通过URI访问资源。资源是被共享的,每个资源只会建立一个对象,没有被引用到则不会建立。
15、应该在xaml中使用Resource资源来放置常量。x:Key 用于识别这些资源。另外还有一个markup extension 名字叫x:Static 专门用于存放静态的Property或字段,也适用于枚举成员。
在任意的资源字典(resource dictionary)内,key应该独一无二。StaticResource是一个特殊的关键词,用在xaml里面查找资源字典中的资源。可以用以下的两种语法实现。
(这里使用double类型的时候,需要引用System命名空间
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="Window1" Height="300" Width="300">
<StackPanel>
<StackPanel.Resources>
<s:Double x:Key="fontsizeLarge">
18.7
</s:Double>
<s:Double x:Key="fontsizeSmall">
14.7
</s:Double>
</StackPanel.Resources>
<Button>
<Button.FontSize>
<StaticResource ResourceKey="fontsizeLarge"/> // 使用了property element语法
</Button.FontSize>
Button with large size
</Button>
<Button FontSize="{StaticResource fontsizeSmall}" > //使用了attribute语法
Button with small size
</Button>
</StackPanel>
C#代码中可以使用Resource.add 和 FindResource方法插入或找到特定key对应的资源。此方法会向上遍历元素树,甚至会检查到Application,因此我们可以并且应该将整 个应用程序都会用到的设定,style和theme放置到Application的资源字典。我们甚至可以在窗体的构造函数内添加新的资源,并在xaml 中使用(如下,虽然这样做并不推荐)。
{
Resources.Add("thicknessMargin", new Thickness(24, 12, 24, 23));
InitializeComponent();
}
<StaticResourceExtension ResourceKey="thicknessMargin"/>
</Button.Margin>
这样我们就可以将对象定义为资源,并利用StaticResource引用这些对象,以及使用x:Static来进行引用静态的Property以及类字段,但是我们不能引用某对象实例的Property和字段,这需要使用数据绑定。
16、资源的Static表达式可以嵌套
Foreground="{StaticResource {x:Static SystemColors.ActiveCaptionBrushKey}}"
这个表达式将前景设置成标题栏的颜色。
Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"/>
而使用DynamicResource,则会在系统的外观改变了以后,将前景色重新改变。DynamicResource主要用来存取系统资源,但资源改变的时候不会有通知,因此需要自己动手了(例如数据绑定)。
17、在多个工程之间共享的资源,可以集中在XAML中,root element是ResourceDictionary,每个资源都是root的孩子。假如有两个或多个这样的XAML需要调用,首先将他们的生成操作设 置为Page 或 Resource。然后在Application.Resource中的MergedDictionaries属性中加入这两个文件。但这种操作会合并两 个文件下的同名资源(后者覆盖前者)。
一切的一切,目标都是减少重复代码。
18、使用数据绑定的attribute语法和property语法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<Label Content="{Binding ElementName=scroll, Path=Value}"/>
<Label>
<Label.Content>
<Binding ElementName="scroll" Path="Value"></Binding>
</Label.Content>
</Label>
19 数据绑定有四种Mode。这些Mode的属性在依赖项属性的metadata中被设定(BindsTwoWayByDefault = true)
OneWay TwoWay OneTime OneWayToSource
OneWay:目标会根据源更新。
TwoWay:双向绑定,任何一方的变化都会导致对方更新。
OneTime: 目标从源取得数据进行初始化,但不会持续跟踪变化。
OneWayToSource: 是OneWay的相反版本,源会根据目标更新。当一个目标没有依赖项属性,但源有依赖项属性的时候,我们就可以把绑定放到源上,将模式设定为OneWayToSource。
但是很奇怪,以下的代码:
<ScrollBar x:Name="scroll2" Orientation="Horizontal" Margin="24" Maximum="100" SmallChange="1" LargeChange="10"
Value="{Binding ElementName=scroll1, Path=Value, Mode=OneWay}"/>
<ScrollBar x:Name="scroll1" Orientation="Horizontal" Margin="24" Maximum="100" SmallChange="1" LargeChange="10"/>
模式设成OneWay 和 OneWayToSource的时候,表现得并不一致。具体原因并不了解。
Path的对象可以是c#中连串的嵌套属性例如string.Length等。对于XAML来说,它只是字符串,并会当作字符串被解析,假如这个东西解析出来是有错误的,解析步骤会中止,但也不报任何异常。
20. 控件模板(control template)继承frameworkTemplate。 而frameworkTemplate 的所有派生类型都是VisualTree 因此,可以在ControlTemplate的tag开始以后直接进行布局。但只要一定义了模板(即定义了控件的Template属性),原来的模板就会 被移除,我们就不能使用默认的外观了。
21. 入门Style只需要知道六个属性的用法。
a.Setter。 Setter可以使用Property和Value字段,设定TargetType中某个属性的值。Value可以是一个数据绑定
b.Resource。 Setter可以通过在Style中定义的Resource,设置Value值(多数情况下,Value值例如一个画刷,不能用字符串代替,此时就应该将Value值拆出,使用<Setter.Value>。
还有一个需要知道的Setter:EventSetter。这个Setter可以将路由事件的处理函数加入到setter,因此可以让几个元素共享一套事件处理。
c.TargetType。 用于指定Style应用的控件,需要使用x:Type来声明(可以当成C#中typeof运算符)。注意只能应用到指定的控件,而不会对派生的类型有任何作用。定义了TargetType,就可以不使用Key关键字,但是这样的话,所有TargetType指定的控件,都会使用这个Style。同时TargetType可以帮助我们精简Property的长度。例如指定了Button控件以后,Button.FontSize 可以写成FontSize。
d. BasedOn。 很多时候,对已有的样式,我们可能需要略加修改以提供更好的体验,那么可以使用BasedOn关键字,重载或者增加一些新的Setter。下面的代码中,normal是一个已定义的属性名
还有一个比较有意思的用法,假如已经有一个Style 定义了对Button的TargetType,那么
就是基于该属性的语法。
e.IsSeal。当样式被使用的时候,只读的IsSeal属性为真,Seal()方法调用,样式不能修改。
f. Trigger。 Trigger赋予我们获知控件的属性改变,并添加处理的能力。
Triggers 是 TriggerCollections,继承的方式如下。
Object
DispatcherObject (abstract)
DependencyObject
TriggerBase (abstract)
DataTrigger
EventTrigger (多用于动画)
MultiDataTrigger
MultiTrigger
Trigger
当Triggers的里面有多个Trigger覆盖了相同的属性的时候,先定义的会被后定义的覆盖。因此在我们无法搞清楚究竟应该使用哪一个顺序或者根本没有可能找到这个顺序的时候,可以使用MultiTrigger,定义多个触发条件<Condition>,当所有的触发条件都成立的时候才做出更改。
DataTrigger 与 Trigger类似,区别在于DataTrigger的触发条件不使用Property,而是使用绑定。
其它:
Setter does not support values derived from Visual or ContentElement。