在许多用户界面技术中,普通控件和自定义绘图之间具有清晰的区别。通常来说,绘图特性只用于特定的应用程序。例如,游戏、数据可视化和物理仿真等。WPF具有一个非常不同的原则。它以相同的方式处理预先构建的控件和自定义绘制的图形。不仅可以使用WPF的绘图支持为用户界面创建付图形的可视化元素,还可以通过他最大限度的利用动画和控件模板等特性。本次分析WPF的2D绘图特性,首先是用于形状回执的基本元素。接着将分析如何使用画刷绘制替他们的边框和内部。然后学习如何使用变换对形状和元素进行旋转、扭曲以及其他操作。最后学习使形状和其他元素半透明。
12.1 理解形状
在WPF用户界面中,绘制2D图形内容的最简单方法是使用形状(shape):专门用于表示简单的直线、椭圆、矩形以及多边形的类。从技术角度来看,形状就是所谓的绘图图元(primitive)。WPF中形状的最重要的细节是,它们都继承自FrameworkElement类。因此,形状是元素。这样的结果是:
- 形状绘制自身。不需要管理无效的情况和绘图过程。例如,当移动内容、改变窗口尺寸或改变形状属性时,不需要手动重新绘制形状。
- 使用与其他元素相同的方式组织形状。也就是可以在任何布局容器中放置一个形状。
- 形状支持与其他元素相同的事件。这意味着为了处理焦点、按下键盘、移动鼠标以及单击鼠标等,不需要进行任何额外的工作。可以使用用于其他元素的相同的事件集,并且同样支持工具提示、上下文菜单和拖放操作。
12.1.1 Shape类
呢个形状都继承自抽象的System.Windows.Shapes.Shape类。如下图显示了形状类的继承层次。
只有很少一部分继承自Shape类。Line、Ellipse以及Rectangle都很直观,Polyline是一系列相互连接的直线Polygon是由一系列相互连接的直线形成的闭合图形。最后,Path类具有非常强大的功能,能够将多个基本形状组合成一个单独的元素。尽管Shape类自身不能执行任何工作,但是它定义了少量的重要属性,如下列表
File | 设置绘制形状表面(边框内的所有内容)的画刷对象 |
Stroke | 设置绘制形状边缘(边框)的画刷对象 |
StrokeThickness | 使用设备无关单位,设置边框的宽度。 |
StrokeStartLineCap 和StrokeEndLineCap |
决定直线开始端和结束端边缘的轮廓。这些属性只影响Line、Polyline以及Path形状。所有其他形状是闭合的,没开始点和结束点 |
StrokeDashArray、 StrokeDashOffset 和StrokeDashCap |
用于在形状周围创建点划线边框。可以控制点划线的尺寸和频率,以及每条点划线开始端和结束端边缘的轮廓 |
StrokeLineJoin和SrokeMiterLimit | 确定形状拐角处的轮廓。对于没有拐角的形状,如Line和Ellipse,这些熟悉不起作用 |
Stretch | 确定形状如何填充可用的区域。可以使用该属性创建能够扩展以适合其容器的形状。还可以为HorizontalAlignment或VerticalAlignmet属性(这些属性继承自FramenworkElement类)使用一个Streth值强制形状在一个方向上扩展 |
DefiningGeometry | 为形状提供一个Geometry对象。Geometry对象描述了形状的坐标和尺寸,不包括UIElement类的相关内容,例如对键盘和鼠标事件的支持。 |
GeometryTransform | 通过该属性可以应用一个Transform对象,改变用于绘制形状的坐标系统。从而可以扭曲、旋转或移动性状。 |
RenderedGeometry | 提供描述最终的、以渲染好的图形的Geometry对象。 |
12.1.2 矩形和椭圆
矩形和椭圆是最简单的形状。为了创建矩形或椭圆,需要设置熟悉的Height和Width属性来定义形状的尺寸,然后设置Fill或Stroke属性使形状可见。如下放置了一个椭圆和一个矩形
<StackPanel> <Ellipse Fill="Yellow" Stroke="Blue" Width="100" Height="50" Margin="5" HorizontalAlignment="Left"></Ellipse> <Rectangle Fill="Yellow" Stroke="Blue" Width="100" Height="50" Margin="5" HorizontalAlignment="Left"></Rectangle> </StackPanel>
Ellipse类没有增加任何属性。Rectangle类只增加了两个属性:RadiusX和RadiusY。如果将这两个属性的值设置为非零值,就可以创建出优美的圆形拐角。随着它们的值的增大矩形拐角部分会更多的被替换。如果一个值大于另一个值的话,值较大的一个方向的拐角会更加平缓,而另一个方向的拐角会更尖锐。如果它们的值分别等于矩形的宽和高时,矩形就会变成椭圆。
12.1.3 改变形状的尺寸和放置形状
硬编码尺寸通常不是创建用户界面的理想方法。它们会限制处理动态内容的能力,并且会使应用程序本地化到去他语言变得更加困难。通常,需要更加严格地控制形状的位置。Ellipse和Rectangle为了适应可用的空间,都能够自动改变它们自身。如果没有提供Height和Width属性,形状会根据他们的容器来设置自身的尺寸。改变形状尺寸的行为依赖于Strech属性的值(该属性是在Shape类中定义的)。默认情况下该属性的值为File。下表中是Stretch属性的所有值。
File | 形状拉伸其宽度和高度以及确切的适应其容器(如果设置了明确的高度和宽度此设置失效) |
None | 形状不被拉伸。除非使用Height和Width属性(或者使用MinHeight和MinWidth)将形状的宽度和高度设置为非0的值,否则不会显示形状 |
Uniform | 按比例改变形状的宽度和高度,直到形状到达容器边缘。 |
UniformToFill | 按比例改变形状的高度和宽度,直到形状填满了整个可用空间的高度和宽度。 |
如下示例,显示了上面三种枚举值的区别
<Ellipse Fill="Yellow" Stroke="Blue" Stretch="Fill"></Ellipse> <Ellipse Fill="Yellow" Stroke="Blue" Grid.Column="1" Stretch="Uniform"></Ellipse> <Ellipse Fill="Yellow" Stroke="Blue" Grid.Column="2" Stretch="UniformToFill "></Ellipse>
WPF形状使用于其他元素相同的布局系统。然而,有些局部容器是不合适的。如StackPanel、DockPanle以及WrapPanel面板,因为它们被设计为独立的元素。Grid面板更加灵活,因为它允许在同一单元格中放置任意多个元素。理想的容器是Canvas,该容器可以完全控制性状如何相互重叠:
<Canvas Grid.Row="1"> <Ellipse Fill="Yellow" Stroke="Blue" Canvas.Left="100" Canvas.Top="50" Width="100" Height="50" ></Ellipse> <Rectangle Fill="Yellow" Stroke="Blue" Canvas.Left="30" Canvas.Top="40" Width="100" Height="50" ></Rectangle> </Canvas>
12.1.4 使用ViewBox控件缩放形状
使用Canvas控件的唯一限制是图形不能改变自身的尺寸以适应更大或更小的窗口。WPF提供了比较容易的解决方法。如果希望联合Canvas控件的精确控制功能和容易的改变尺寸的功能,可以使用Viewbox元素。Viewbox是一个继承自Decorator的简单类。该类只接受一个子元素,并且拉伸或缩小其子元素以适应可用的空间。Viewbox经常用于矢量图形。如下示例
<Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <TextBlock>The first row of a grid.</TextBlock> <Viewbox Grid.Row="1" HorizontalAlignment="Left" MaxHeight="500"> <Canvas Width="200" Height="150"> <Ellipse Fill="Yellow" Stroke="Blue" Canvas.Left="10" Canvas.Top="50" Width="100" Height="50" HorizontalAlignment="Left"></Ellipse> <Rectangle Fill="Yellow" Stroke="Blue" Canvas.Left="30" Canvas.Top="40" Width="100" Height="50" HorizontalAlignment="Left"></Rectangle> </Canvas> </Viewbox> </Grid>
Viewbox元素按比例的执行缩放,保持它所包含内容的纵横比。这意味着即使包含的形状发生了变化,内部的形状不会变形。可以使用Viewbox.Stretch属性改变该行为。该属性可以使用Stretch的所有枚举值。还可以通过使用StretchDirection属性获得更大的控制。默认情况下,该属性值被设置为Both,但可以使用UnOnly值创建只会增加而不会收缩超过其元素尺寸的内容,并且可以使用DownOnly创建只会缩小而不会增长的内容。为使Viewbox元素执行其缩放工作,他需要能够确定两部分信息:1、新尺寸。Viewbox元素根据其Stretch属性,让其内部的内容使用所有可用的空间。2、原始尺寸,不使用Viewbox控件时的尺寸,隐含在定义嵌套内容的方式中。
12.1.5 直线
Line形状表示连接一个点和另一个点的一条直线。起点和终点由4个属性设置:X1与Y1和X2与Y2。例如,下面是一条从点(0,0)伸展到点(10,100)的直线
<Line Stroke="Blue" X1="0" Y1="0" X2="10" Y2="100"></Line>
对于直线,Fill属性不起作用,必须设置Stroke属性。在直线上使用的坐标是相对于放置直线的矩形区域左上角的坐标。在直线中使用负的坐标值是非常合理的。可以为直线使用使其超出为直线保留的空间的坐标,从而在窗口中其他任意部分上绘制直线。但是直线不能使用流内容模型。这意味着直线设置Margin、HorizontalAlignment以及VerticalAlignment属性是没有意义的。对于Polyline和Polygon形状具有同样的限制。
12.1.6 折线
通过Polyline类可以绘制一系列相互连接的直线。只需要使用Points属性提供一系列X和Y坐标。Points属性需要一个PointCollection对象,但是在XAML中使用基于简单字符串的语法填充改集合。只需要提供一个点的列表,并在每个坐标之间添加空格或逗号。为了更便于阅读,可在每个X和Y坐标之间使用逗号。如下示例:
<Polyline Stroke="Blue" StrokeThickness="5" Points="10,150 30,140 50,160 70,130 90,170 110,120 130,180 150,110 170,190 190,100 210,240" > </Polyline>
12.1.7 多边形
Polygon和Polyline是相同的。和Polyline类一样,Polygon类也有一个包含一系列坐标的Points集合。唯一区别是Polygon形状添加最后一条线段,将最后一个点连接到开始点。可以使用Fill画刷填充该形状的内部区域。
<Polygon Stroke="Blue" StrokeThickness="5" Fill="Yellow" Canvas.Top="200" Points="10,150 30,140 50,160 70,130 90,170 110,120 130,180 150,110 170,190 190,100 210,240" > </Polygon>
12.1.8 直线线帽和直线交点
当绘制Line和Polyline形状时,可以使用StartLineCap和EndLineCap属性选择如何绘制直线的开始端和结束端。StarLineCap和EndLineCap属性通常都被设置为Flat,这意味着直线在它的最后坐标处立即终止。其他选择包括Round(该设置会平滑的绘制拐角)、Triangle(绘制直线的两条侧边最后交于一点),以及Square(该设置使直线端点具有尖锐的边缘)。这三个设置都会增加直线的长度额外的距离是直线宽度的一半。如下是以上四种的结束端的展示
<Grid Margin="15"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> </Grid.ColumnDefinitions> <Polyline Stroke="Blue" StrokeThickness="15" StrokeEndLineCap="Flat" SnapsToDevicePixels="True" Points="10,10 30,0 50,20 90,10 200,10" > </Polyline> <TextBlock Grid.Column="1">Flat Line Cap</TextBlock> <Polyline Stroke="Blue" StrokeThickness="15" Grid.Row="1" StrokeEndLineCap="Square" SnapsToDevicePixels="True" Points="10,10 30,0 50,20 90,10 200,10" > </Polyline> <TextBlock Grid.Row="1" Grid.Column="1">Square Line Cap</TextBlock> <Polyline Stroke="Blue" StrokeThickness="15" Grid.Row="2" StrokeEndLineCap="Round" SnapsToDevicePixels="True" Points="10,10 30,0 50,20 90,10 200,10" > </Polyline> <TextBlock Grid.Row="2" Grid.Column="1">Round Line Cap</TextBlock> <Polyline Stroke="Blue" StrokeThickness="15" Grid.Row="3" StrokeEndLineCap="Triangle" SnapsToDevicePixels="True" Points="10,10 30,0 50,20 90,10 200,10" > </Polyline> <TextBlock Grid.Row="3" Grid.Column="1">Triangle Line Cap</TextBlock> </Grid>
除了Line形状之外,所有的形状都允许使用StrokeLineJoin属性扭曲它们的拐角,有三种选择。默认值是Miter,使用尖锐的边缘,而Bevel值切掉点边缘,Round值平滑的过渡边缘。当为较宽并且角度非常小的直线拐角使用尖锐的边缘时,尖锐的拐角会不切实际的延伸很长距离,可以使用Bevel或Round设置修建拐角。或者可以使用SrokeMiterLimit属性,当达到一个特定的最大长度时,该属性自动的剪切拐角StrokeMiterLimit属性是一个系数,该系数是用于锐化拐角的长度和直线宽度的一半的比值。如下
<Grid Margin="15"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> </Grid.ColumnDefinitions> <Polyline Stroke="Blue" StrokeThickness="14" StrokeLineJoin="Bevel" SnapsToDevicePixels="True" Points="10,60 30,10 50,70 90,40" > </Polyline> <TextBlock Grid.Column="1" VerticalAlignment="Center">Bevel Line Join</TextBlock> <Polyline Stroke="Blue" StrokeThickness="14" Grid.Row="1" StrokeLineJoin="Round" SnapsToDevicePixels="True" Points="10,60 30,10 50,70 90,40" > </Polyline> <TextBlock Grid.Row="1" Grid.Column="1" VerticalAlignment="Center">Round Line Join</TextBlock> <Polyline Grid.Row="2" Stroke="Blue" StrokeThickness="14" StrokeLineJoin="Miter" StrokeMiterLimit="1" SnapsToDevicePixels="True" Points="10,60 30,10 50,70 90,40" > </Polyline> <TextBlock Grid.Row="2" Grid.Column="1" VerticalAlignment="Center">Miter Line Join</TextBlock> <Polyline Grid.Row="3" Stroke="Blue" StrokeThickness="14" StrokeLineJoin="Miter" StrokeMiterLimit="3" SnapsToDevicePixels="True" Points="10,60 30,10 50,70 90,40" > </Polyline> <TextBlock Grid.Row="3" Grid.Column="1" VerticalAlignment="Center">Miter Line Join With Limit of 3</TextBlock> </Grid>
12.1.9 点划线
除了为形状的边框绘制乏味的实线之外,还可以绘制点划线——根据指定的模式使用空白断开的直线。当在WPF中创建一条点划线时,不限制进行特定的预先设置。反而,可以通过设置StrokeDashArray属性选择实线段的长度和断开空间的长度,如下
<Polyline Stroke="Blue" StrokeThickness="10" StrokeDashArray="1 2" Points="10,30 60,0 90,40 120,10 350,10" SnapsToDevicePixels="True"> </Polyline>
这条点划线的实线段长度值为1,空白长度值为2.这些值是相对于直线宽度的。如果直线宽度是14个单位,实线部分的长度为14单位,后面是28单位的空白部分。直线在其整个长度中重复这种模式。不见得非要使用整数值,如下StrokeDashArray="5 0.2 3 0.2"
12.1.10 像素对齐
WPF使用的是设备无关的绘图系统。为自体和形状等内容指定的数值使用“虚拟”像素,在通常的96dpi显示器上,“虚拟”像素和正常像素的大小相同,但是在更高dpi的显示器上其尺寸会被缩放。设备无关单位和屋里像素之间的转换会自动进行,并且通常根本不需要考虑这个问题。不同dpi设置之间的像素比很少是整数。当以设备无关单位提供数值时,WF始终允许使用非整数的双精度值。显然,无法在像素之间的点上纺织一条边缘。WPF通过使用反锯齿特性进行补偿。例如:当绘制一条62.4992个像素的红线时,WPF可以正常填充前62个像素,然后使用直线颜色和背景颜色之间的颜色为第63个像素着色。但在此存在一个问题。如果正在绘制直线、矩形或具有指教的多边形,这种自动反锯齿特性会在形状边缘导致一个模糊区域。
根据正在绘制的图形类型,模糊边缘可能看起来很正常。然而,如果不希望这种行为,可以让WPF不要为特定的形状使用反锯齿特性进行处理,反而WPF会将尺寸摄入到最近的设备像素。可以通过将UIElement类的SnapsToDevicePixels属性设置为true来启用这个称为像素对齐的特性。
12.2 画刷
画刷填充区域,不管是元素的背景色、前景色以及边框,还是形状的内部填充和笔画。最简单的画刷类型是SolidColorBrush,这种画刷填充一种固定的、连续颜色。在XAML中设置形状的Stroke或Fill属性时,使用的是SolidColorBrush画刷,它们在后台完成绘制。以下是几个与画刷相关的更基本的方面:
- 画刷支持更改通知,因为它们继承自Freezable类。因此,如果改变了画刷,任何使用该画刷的元素都会自动重新绘制自身。
- 画刷支持部分透明。为了使用半透明画刷,只需修改Opacity属性,使背景能够透过前面的内容进行显示。
- 通过SystemBrushes类可以访问这样的画刷:此类画刷使用Windows系统设置为当前计算机定义的首选颜色。
SolidColorBrush画刷非常有用,但还有其他几个继承自System.WindowsMedia.Brush,并且通过这些类可以得到更新颖的效果。如下表所列
SolidColorBresh | 使用单一的连续颜色绘制区域 |
LinearGradientBrush | 使用渐变填充绘制区域,渐变的阴影填充从一种颜色变化到另一种颜色(并且,可以变化到低三种颜色之后在变化到第四种颜色,以此类推) |
RadialGradientBrush | 使用镜像渐变填充绘制区域,除了是在圆型模式中从中心点向外部辐射渐变之外,这种画刷和线性渐变画刷类似 |
ImageBrush | 使用可以被拉伸、缩放或平铺的图像绘制区域 |
DrawingBrush | 使用一个Drawing对象绘制区域。该对象可以包含已经定义的形状和位图 |
VisualBrush | 使用一个Visual对象绘制区域。因为所有WPF元素都继承自Visual类,所以可以使用该画刷将部分用户界面(如按钮的表面)复制到另外一个区域。当创建特殊效果时,如部分反射效果,该画刷特别有用 |
BitmapCacheBrush | 使用从一个Visual对象缓存的内容绘制区域。这种画刷和VisualBrush类似,但是如果需要在多个地方重用图形内容或者频繁地重绘图形内容,这种画刷更高效。 |
12.2.1 SolidColorBrush画刷
在大多数控件中,设置Foreground属性绘制文本颜色,并设置Background属性绘制文本后面的空间。星装饰用类似但不同的属性:Stroke属性用于绘制形状的边框并且Fill属性用于绘制形状的内部。可以在XAML中使用颜色名称设置Stroke和Fill属性,对于这种情况WPF解析器自动创建匹配的SolidColorBrush对象。也可以使用代码设置Stroke和Fill属性,但是需要显示地创建SolidColorBrush对象:
cmd.Background=new SolidColorBrush(Colors.AliceBlue); cmd.Background=SystemColors.ControlBrush; int red=0; int green=255; int blue=0; cmd.Foreground=new SolidColorBrush(Color.FromRgb(red,greed,blue));
12.2.2 LinearGradientBrush画刷
通过LinearGradientBrush画刷可以创建从一种颜色变化到另一种颜色的混合填充。下面是最简单的渐变。该渐变从蓝色到白色在对角线上队举行进行着色:
<Rectangle Width="150" Height="100" Margin="5"> <Rectangle.Fill> <LinearGradientBrush > <GradientStop Color="Blue" Offset="0"/> <GradientStop Color="White" Offset="1" /> </LinearGradientBrush> </Rectangle.Fill> </Rectangle>
为了创建这种渐变,需要为每种颜色添加一个GradientStop,还需要为每种颜色添加一个GradientStop,还需要在渐变中使用从0~1的偏斜值放置每种颜色。在该示例中,用于蓝色的GradientStop对象的偏移值为0,这意味着它被放置在渐变的开头。用于白色的GradientStop对象的偏移值为1,这意味着将它放在末尾。通过改变这些值,可以调整渐变从一种颜色变化多另一种颜色的速度。例如如果将白色的GradientStop设置为0.5,渐变就会在中间(对角线)从蓝色(左上角)混合到白色。代码如下:
<Rectangle Width="150" Height="100" Margin="5" Grid.Row="1"> <Rectangle.Fill> <LinearGradientBrush> <GradientStop Color="Blue" Offset="0"/> <GradientStop Color="White" Offset="0.5" /> </LinearGradientBrush> </Rectangle.Fill> </Rectangle>
上面的标记创建了一个从一个拐角拉伸到另一个拐角的对角填充渐变。如果希望创建从顶部向底部或一边向另一边混合的渐变,或者是使用不同的对角线角度。可以使用LinearGradientBrush的StartPoint和EndPoint属性控制这些细节。通过这些属性可以选择第一种颜色开始变化的点,以及最后一种颜色结束变化的点。但在次存在一个古怪的问题:用于开始点和结束点的坐标不是真实的坐标。反而,LinearGradientBrush画刷将点(0,0)指定为希望填充的区域的左上角,并且将点(1,1)指定为向填充的区域的右下角,而不管该区域实际上有多高和多宽。为了创建从上到下的横向填充,可以将用于左上角(0,0)的点作为开始,并将(0,1)点作为结束,为了创建从一边到另一边的垂直填充,可以使用点(0,0)作为开始点,到(1,0)点作为结束点。
通过为渐变提供不是填充区域拐角点的开始点和结束点,可以得到更加灵活的渐变。例如,渐变可以从点(0,0)拉伸到点(0,0.5),该点位于左侧边缘上的中点。这会创建一个压缩的线性渐变 — 一种颜色从顶部开始,在中间混合到第二种颜色。形状的后半部分使用第二种颜色填充。但是可以使用LinearGradientBrush.SpeadMethod属性改变这种行为。默认情况下,该属性被使用Pad(渐变之外的区域使用恰当的纯色填充),但是也可以使用Reflect(翻转渐变,从第二种颜色反向渐变到第一种颜色)或设置为Repeat(复制相同颜色变化过程)。如下是使用Reflect效果
<Rectangle Width="150" Height="100" Grid.Row="3" Margin="5"> <Rectangle.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="0,0.5" SpreadMethod="Reflect"> <GradientStop Color="Blue" Offset="0"/> <GradientStop Color="White" Offset="1" /> </LinearGradientBrush> </Rectangle.Fill> </Rectangle>
LinearGradientBrush.SpreadMethod画刷还可以通过添加两个以上的GradientStop对象,创建具有两种以上颜色的渐变。例如以下的渐变效果
<Rectangle Width="150" Height="100" Grid.Row="4" Margin="5"> <Rectangle.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Yellow" Offset="0.0" /> <GradientStop Color="Red" Offset="0.25" /> <GradientStop Color="Blue" Offset="0.75" /> <GradientStop Color="LimeGreen" Offset="1.0" /> </LinearGradientBrush> </Rectangle.Fill> </Rectangle>
但是需要注意的是GradientStop设置合适的偏移值。
渐变画刷并不局限于只能用于绘制形状。可以在使用SolidColorBrush画刷的任何时候替换LinearGradientBrush。例如,填充一个元素的背景表面、填充元素文本的前景色,或者填充边框。如下是显示了一个渐变填充TextBlock控件的示例
<TextBlock Margin="5" FontWeight="Bold" FontSize="65" TextWrapping="Wrap" TextAlignment="Center"> <TextBlock.Text>This text uses a gradient.</TextBlock.Text> <TextBlock.Foreground> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Yellow" Offset="0.0" /> <GradientStop Color="Red" Offset="0.25" /> <GradientStop Color="Blue" Offset="0.75" /> <GradientStop Color="LimeGreen" Offset="1.0" /> </LinearGradientBrush> </TextBlock.Foreground> </TextBlock>
12.2.3 RadialGradientBrush画刷
RadialGradientBrush画刷和LinearGradientBrush画刷的工作方式类似。他也使用一系列具有不同偏移值的颜色。和LinearGradientBrush画刷一样,可以使用希望的人一多种颜色。区别是放置渐变的方式。为了指定第一种颜色在渐变中的开始点,需要使用GradientOrigin属性。默认情况下,渐变的开始点是(0.5,0.5),该点表示填充区域的中心。渐变从开始点以环形的方式向外辐射。最终,渐变到达内部渐变圆的边缘,这是渐变的终点。渐变圆的中心可能和渐变开始点对齐也可能和渐变开始点不对齐。超出内部渐变圆的区域以及填充区域的最外侧边缘,使用在RadialGradientBrush.GradientStops集合中的最后一种颜色进行纯色填充。可以使用三个属性设置内部渐变圆的边界:Center、RadiusX和RadiusY。默认情况下,Center属性被设置为(0.5,0.5),该设置将限定圆的中心放置在填充区域的*,并且该点同时也是渐变开始点。RadiusX和RadiusY水洗那个决定了限定圆的尺寸,并且在默认情况下这两个属性都被设置为0.5。这些值可能有些不直观,因为他们根据填充区域的对角范围进行度量。对于填充圆形形状并创建发光效果,径向渐变是一个非常好的选择。稍微偏移GradientOrigin点是一种常见的做法,这样可以使形状创建深度感。
<Ellipse Margin="5" Stroke="Black" StrokeThickness="1"> <Ellipse.Fill> <RadialGradientBrush RadiusX="1" RadiusY="1" > <GradientStop Color="White" Offset="0" /> <GradientStop Color="Blue" Offset="1" /> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> <Ellipse Margin="5" Grid.Row="1" Stroke="Black" StrokeThickness="1"> <Ellipse.Fill> <RadialGradientBrush RadiusX="1" RadiusY="1" GradientOrigin="0.7,0.3" > <GradientStop Color="White" Offset="0" /> <GradientStop Color="Blue" Offset="1" /> </RadialGradientBrush> </Ellipse.Fill> </Ellipse>
12.2.4 ImageBrush画刷
通过ImageBrush画刷可以使用位图填充区域。可以使用最常见的文件类型,包括BMP、PNG、GIF以及JPEG文件。可以通过设置ImageSource属性指定希望使用的图像。对于ImageBrush,其ImageSource属性和Image元素的Source属性的工作方式相同。为了使图像画刷达到一定效果,我们还可以设置ImageBrush.Stretch属性。还可以使用Viewbox属性从图像上裁剪有兴趣使用的小部分。为此,需要指定4个数值描述希望从源图像上剪裁并使用的矩形部分。前两个数值指定矩形开始的左上角,而后两个数值指定矩形的宽度和高度。如下示例中,我们从一个图像上截取了一个正方形图像,该图像从源图像的X坐标的0.4倍和Y坐标高度的0.5倍开始,然后伸展矩形填充一个20%宽度和20%高度的方框作为截图。
<Rectangle Canvas.Left="10" Canvas.Top="10" Width="271" Height="100" Stroke="Black"> <Rectangle.Fill> <ImageBrush ImageSource="logo.gif" Stretch="None"></ImageBrush> </Rectangle.Fill> </Rectangle> <Rectangle Canvas.Left="10" Canvas.Top="120" Width="200" Height="200" Stroke="Black"> <Rectangle.Fill> <ImageBrush ImageSource="logo.gif" Stretch="Uniform" Viewbox="0.4 0.5 0.2 0.2"></ImageBrush> </Rectangle.Fill> </Rectangle>