WPF,Silverlight与XAML读书笔记第三十四 - 可视化效果之2D变换
说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。
Transform - 变换主要用于实现旋转缩放等效果,WPF中2D变换由System.Windows.Media命名空间的Transform类来处理。应用变换会影响元素的尺寸和位置。
每个FrameworkElement都有如下两个类型的Transform属性,这些是变换可以应用的对象:
-
LayoutTransform:其在对元素布局以前被应用。
-
RenderTransform:(FrameworkElement继承自UIElement获得)其在布局结束后,元素被在渲染前应用。
另外我们也可以通过Brush.Transform属性将变化应用到Brush, 包括像IamgeBrush等。通过定义Geometry.Transform属性可以对集合形状进行变形等等。
这两个属性的变换信息可以通过<TransformGroup>元素来设置,这其中可以定义单个变换或一组变换的集合。(如果只定义一个转换,可以不写<TransformGroup>而只是写具体的转换)。
以上这两种不同类型变化的效果可以由下列示例看出(以RotateTransform示例):
前者为LayoutTransform,后者为RenderTransform:
XAML:
1 <StackPanel Width="66" HorizontalAlignment="Left"> 2 <Button>Top</Button> 3 <Button> 4 <Button.LayoutTransform> 5 <RotateTransform Angle="45" /> 6 </Button.LayoutTransform> 7 Middle 8 </Button> 9 <Button>Bottom</Button> 10 </StackPanel> 11 <StackPanel Width="66"> 12 <Button>Top</Button> 13 <Button> 14 <Button.RenderTransform> 15 <RotateTransform Angle="45" /> 16 </Button.RenderTransform> 17 Middle 18 </Button> 19 <Button>Bottom</Button> 20 </StackPanel>
运行效果图:
如示例可以看出,布局前发生的变换(FlyoutTransform)会把其他元素挤开。而布局后发生的变换(RenderTransform)不会影响其他元素的布局位置。
UIElement(FrameworkElement)中有一个RenderTransformOrigin属性表示变换的开始点(在变换过程中这个点保持固定)。(LayoutTransform没有同等的概念,被变换元素定位由父面板布局规则控制。)
RenderTransformOrigin属性类型为System.Windows.Point,默认值为(0,0),表示左上角,另外(0,1)表示左下角,(1,0)表示右上角,(1,1)表示右下角。如果数字大于1则表示原点在元素之外,另外数字小于1表示原点在元素内部,如(0.5,0.5)表示元素中心。
XAML中,可以通过RenderTransformOrigin="0.5,0.5",这样的方式来设置,这主要归功于System.Windows.PointConverter。
变化主要用于产生一些动画等效果,下面将逐个介绍WPF中内建的2D变换效果,这些变换的类都位于System.Windows.Media命名空间中:
1. RotateTransform
RotateTransform用于在X-Y平面以特定点进行特定角度的旋转。主要属性介绍如下:
如下3个浮点类型属性控制元素的旋转:
-
Angle:旋转角度,单位为度数,默认值为0,默认旋转方向为顺时针。
-
CenterX:旋转的水平中心,默认值为0。
-
CenterY:旋转的垂直中心,默认值为0。
水平旋转中心与垂直旋转中心相结合即是旋转点,由上可看出这点默认值为(0,0),即包含选择元素的容器的左上角。
注意,(CenterX, CenterY)只有RotateTransform被应用为RenderTransform时才有效,因为这两个属性影响元素的布局,而LayoutTransform的布局是受父元素控制的,所以对这些属性不敏感。
细节:UIElement的RenderTransformOrigin属性与RotateTransform的CenterX与CenterY属性的不同。
CenterX与CenterY可以用绝对坐标定位原点,而RenderTransform只允许用相对定位。这个区别可以举这样一个小例子:如一个宽度为20的元素,其右上角用CenterX和CenterY表示则分别为20,0。而RenderTransformOrigin表示则为(1,0)。
当RenderTransform包含多个Transform时,RenderTransformOrigin会对所有这些Transform产生影响。当这其中包含有RotateTransform且其CenterX和CenterY都有设置时,它们将被组合来计算最终的原点。 当CenterX和CenterY用于数据绑定时,将比RenderTransformOrigin类型方便。 如果是围绕元素的中心做变换等情况,则RenderTransformOrigin这种相对的方式更简单。如果用CenterX和CenterY完成,则需要编程计算得到绝对位置。
RenderTransform也可以用于内容控件其中的内容,可以参考如下例子:
1 <Button> 2 <TextBlock RenderTransformOrigin="0.5,0.5"> 3 <TextBlock.RenderTransform> 4 <RotateTransform Angle="45"/> 5 </TextBlock.RenderTransform> 6 Middle 7 </TextBlock> 8 </Button>
RenderTransform构造函数有如下两类重载,可以接受这样的参数:
-
一个角度值
-
一个角度值,一个中心值
2. ScaleTransform
在X-Y平面进行缩放,即将元素在水平,垂直,或者水平垂直两个方向上扩大或者缩小一个元素。
其有4个浮点类型的属性控制这个功能:
-
ScaleX:元素宽度的缩放倍数
-
ScaleY:元素高度的缩放倍数
-
CenterX:水平缩放的原点
-
CenterY:垂直缩放的原点
属性的使用上,0.5的ScaleX/ScaleY会把水平/垂直方向缩小一半,而大小为2的ScaleX/ScaleY会把水平垂直大小放大一倍。而CenterX与CenterY则与RotateTransform中同名属性一样只在RenderTransform模式下有效。
另外显而易见的是CenterX只有在ScaleX不为1时才能起作用。CenterY只有当ScaleY不为1时才起作用。CenterX与CenterY的默认值均为0,即变换元素所在容器的左上角,这样默认情况下水平向缩放会向右进行,垂直向缩放会向下进行。当CenterX与CenterY被设置为大于0的值后,变换也就会同时向左与向上进行。
ScaleTransform也有重载的构造函数以方便在代码中创建变换。
提示:ScaleTransform与Stretch Alignment之间的交互。
当把ScaleTransform作为LayoutTransform应用到一个已经在伸缩尺度内拉伸的元素上,只有当ScaleTransform拉伸的量比控件本身的拉伸量大时,这个变换才会有效果。
提示:ScaleTransform变换对FrameworkElement的ActualHeight与ActualWidth,及对UIElement的RenderSize属性的影响。
如前文所述,控件的ActualHeight属性,ActualWidth属性的值与RenderSize的Height,Width属性分别都是相同的。在应用ScaleTransform变换后,虽然可视化效果上改变了,但这些属性值都不会被改变,是一种"虚报"的效果。
提示:ScaleTransaction对Margin与Padding的影响
Padding与内部内容会被一起缩放,但是Padding值不会变化,虽然视觉效果发生变化,这类似上一提示中的"虚报"现象。Margin不会受此影响。
最后仍然是一段简单的代码:
1 <Rectangle Fill="Pink" Stroke="Azure" Width="88" Height="66"> 2 <Rectangle.RenderTransform> 3 <ScaleTransform ScaleX="2" CenterX="30" /> 4 </Rectangle.RenderTransform> 5 </Rectangle>
3. SkewTransform
通过4个浮点属性来按元素的X轴与Y轴进行倾斜。直观上来说如果是一个正方形进行SkewTransform变换后看起来为一个平行四边形。
-
AngleX:水平倾斜的角度,默认值为0。
-
AngleY:垂直倾斜的角度,默认值为0。
-
CenterX:水平倾斜的原点。默认值为0。
-
CenterY:垂直倾斜的原点。默认值为0。
这些属性与ScaleTransform中的属性,用法非常类似,这里不再赘述,给出一段代码:
1 <Rectangle Fill="Pink" Stroke="Azure" Width="88" Height="66"> 2 <Rectangle.RenderTransform> 3 <SkewTransform AngleX="36" /> 4 </Rectangle.RenderTransform> 5 </Rectangle>
4. TranslateTransform
沿X轴或Y轴将对像移动指定的距离,有两个浮点属性控制元素移动。
-
X:水平移动量,默认值为0
-
Y:垂直移动量,默认值为0
在LayoutTransform下此变化没有任何效果,在RenderTransform利用此变化可以按用户的操作来形成动画效果。当X,Y为正值时,变换对象将分别向右方与下方移动,反之为负值时将向左方与上方移动。
给出一段简单的代码:
1 <Rectangle Fill="Pink" Stroke="Azure" Width="88" Height="66"> 2 <Rectangle.RenderTransform> 3 <TranslateTransform X="50" Y="-50"/> 4 </Rectangle.RenderTransform> 5 </Rectangle>
5. MatrixTransform:
MatrixTransform用于自定义2D变换效果。这种变换效果的本质是一个System.Windows.Media.Matrix类型的Matrix属性。这种类型可以表示一个各元素的含义如下的3x3的矩阵:
其中矩阵最后一列值是固定的(0 0 1),而前两列值可由Matrix类型属性来设置(前两列的这些属性的名称与Matrix类型中的属性同名)。
所有之前介绍的变换都可以使用MatrixTransform来表示。
提示:MatrixTransform的类型转换器
MatrixTransform是唯一一个拥有类型转换器(TransformConverter这个转换器)的变换,这样在XAML中就可以使用一个字符串来设置Matrix属性。例如:
<Button RenderTransform="1,0,0,1,10,20" />
这条语句将按钮向右移动10个元素,向下移动20个元素。
深入分析其中关系,有如下对应:
所以很容易看出,"1,0,0,1,0,0 "就表示不做变换,而上文的设置就相当于设置了TranslateTransform的X属性与Y属性。而设置缩放的话可以设置第一个与第四个值,作用就相当于设置ScaleTransform的ScaleX与ScaleY。
给出一个简单的例子:
1 <Rectangle Fill="Pink" Stroke="Azure" Width="88" Height="66"> 2 <Rectangle.RenderTransform> 3 <MatrixTransform Matrix="1 0 1 2 0 1"/> 4 </Rectangle.RenderTransform> 5 </Rectangle>
运行时效果如下图:
变换组合
有多种方式可以将变换组合起来:
-
同时使用LayoutTransform变换和RenderTransform变换。
-
也可以使用一个适当的MatrixTransform来表示多条转换。
-
使用TransformGroup类。
TransformGroup类是一个派生自Transform的类(所以可以像使用ScaleTransform或TranslateTransform等一样使用TransformGroup完成RenderTransform或LayoutTransform的设置),但TransformGroup类主要为组合"变换"而存在。这种组合通过设置TransformGroup的Children集合属性来完成。而Children属性作为一个内容属性在XAML就可以使用如下方式来设置:
1 <Button> 2 <Button.RenderTransform> 3 <TransformGroup> 4 <RotateTransform Angle="45" /> 5 <ScaleTransform ScaleX="5" ScaleY="1"/> 6 <SkewTransform AngleX="30"/> 7 </TransformGroup> 8 </Button.RenderTransform> 9 OK 10 </Button>
这段代码对目标对象进行了基于X轴45的旋转,在X轴方向进行5倍放大,基于X轴进行30度的倾斜。
内幕:WPF处理TransformGroup的方式
在内部WPF首先将TransformGroup计算为一个类似MatrixTransform的Matrix属性类似的值并将其应用到目标元素上,以提高性能。
提示:可以将多个相同类型的变换应用到一个TransformGroup中。如,两个45度的RotateTransform放在一起可以产生一个90度RotateTransform的效果。
提示:对变换的支持。
所有宿主非WPF的元素不支持以上这些变化,如后文要介绍的HwndHost。另外Frame这个用来承载Html内容的元素本身支持变换,但其发生变换时内部元素不会发生变换。
本文完
参考:
《WPF揭秘》