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属性的不同。

  1. CenterX与CenterY可以用绝对坐标定位原点,而RenderTransform只允许用相对定位。这个区别可以举这样一个小例子:

    如一个宽度为20的元素,其右上角用CenterX和CenterY表示则分别为20,0。而RenderTransformOrigin表示则为(1,0)。

  2. 当RenderTransform包含多个Transform时,RenderTransformOrigin会对所有这些Transform产生影响。当这其中包含有RotateTransform且其CenterX和CenterY都有设置时,它们将被组合来计算最终的原点。
  3. 当CenterX和CenterY用于数据绑定时,将比RenderTransformOrigin类型方便。
  4. 如果是围绕元素的中心做变换等情况,则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>

运行时效果如下图:

 

变换组合

有多种方式可以将变换组合起来:

  1. 同时使用LayoutTransform变换和RenderTransform变换。
  2. 也可以使用一个适当的MatrixTransform来表示多条转换。
  3. 使用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揭秘》

posted @ 2012-12-24 09:05  hystar  阅读(249)  评论(0编辑  收藏  举报