【WPF学习】第四十章 画刷

  画刷填充区域,不管是元素的背景色、前景色以及边框,还是形状的内部填充和笔画(Stroke)。最简单的画刷类型是SolidColorBrush,这种画刷填充一种固定、连续的颜色。在XAML中设置形状的Stroke或Fill属性时,使用的是SolidColorBrush画刷,他们在后台完成绘制。

  下面是几个与画刷相关的更基本的方面:

  •   画刷支持更改通知,因为他们继承自Freezable类。因此,如果改变了画刷,任何使用画刷的元素都会自动重新绘制自身。
  •   画刷支持部分透明。为此,只需要修改Opacity属性,使背景能够透过前面的内容进行显示。
  •   通过SystemBrushes类可以访问这样的画刷:此类画刷使用Windows系统设置为当前计算机定义的首选颜色

  SolidColorBrush画刷无疑非常有用,但还有其他几个继承自System.Windows.Media.Brush的类,通过这些类可得到更新颖的效果。下表列出了所有这些类。

表 画刷类

一、SolidColorBrush画刷

  在大多数控件中,通过设置Foreground属性绘制文本颜色,并设置Background属性绘制文本背后的空间。形状使用类似但不同的属性:Stroke属性用于绘制形状的边框,而Fill属性用于绘制形状的内部。

  可在XAML中使用颜色名设置Stroke和Fill属性,对于这种情况,WPF解析器自动创建匹配的SolidColorBrush对象。也可以使用代码设置Stroke和Fill属性,但需要显示地创建SolidColorBrush对象。

//Create a brush from a named color:
cmd.Background=new SolidColorBrush(Colors.AliceBlue);

//Create a brush from a system color:
cmd.Background=SystemColors.ControlBrush;

//Create a brush from color values:
int red=0;int green=255; int blue=0;
cmd.Foreground=new SolidColorBrush(Color.FromRgb(red,green,blue));

二、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对象,还需要在渐变中使用0~1的偏移值放置每种颜色。在该例中,用于蓝色的GradientStop对象的偏移值为0,这意味着它被放在渐变的开头。用于白色的GradientStop对象的偏移值为1,这意味着将它放在末尾。通过改变这些值,可调整渐变从一种颜色变化到另一种颜色的速度。例如,如果将白色的GradientStop设置为0.5,渐变就会在中间(两个拐角的中点)从蓝色(左上角)混合到白色。矩形的右边将会是纯白色的。

  上面的标记创建了从一个拐角拉伸到另一个拐角的对象填充渐变。然而,可能希望创建自上而下或从一边向另一边混合的渐变,或是使用不同的对角线角度。可使用LinearGradientBrush的StartPoint和EndPoint属性控制这些细节。可以通过这些属性选择第一种颜色开始变化的点,以及最后一种颜色结束变化的点(中间的区域被渐变混合)。但这里存在一个古怪的问题:用于开始点和结束点的坐标不是真实坐标。相反,LinearGradientBrush画刷将点(0,0)指定为希望希望填充的区域的左上角,将点(1,1)指定为希望填充的区域的右下角,而不管该区域实际上有多高和多宽。

  为创建自上而下的横向填充,可将用于左上角的(0,0)点作为开始点,并将(0,1)点作为结束点,该点表示左下角。为了创建从一边到另一边的垂直填充(不倾斜),可以使用点(0,0)作为开始点,并使用右上角的点(1,0)作为结束点。

  通过为渐变提供不是填充区域拐角点的开始点和结束点。可得到更灵活的渐变。例如,渐变可从点(0,0)拉伸到点(0,0.5),该点是左侧边缘上的中点。这会创建压缩的线性渐变——一种颜色从顶部开始,在中间混合到第二种颜色。形状的后半部分使用第二种颜色。但可用LinearGradientBrush.SpreadMethod属性改变这种行为。默认情况下,该属性使用Pad(这意味着渐变之外的区域使用恰当的纯色填充),但也可使用Reflect(翻转渐变,从第二种颜色反向渐变大偶第一种颜色)或Repeat(复制相同的颜色变化过程)。

  LinearGradientBrush画刷还可通过添加两个以上的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对象何止合适的偏移值。例如,如果希望变换经过5中颜色,可将第1中颜色的偏移值设置为0,第2中颜色的偏移值设置为0.25,将第3中颜色的偏移值设置为0.5,第4种颜色的偏移值设置为0.75,将第5中颜色的偏移值设置为1。或者如果希望开始时渐变速度较快,而在结束速度较慢,可将便宜值设置为0、0.1、0.2、0.4、0.6、1。

  下面是LinearGradientBrush画刷的完整示例以及效果图:

<Window x:Class="Drawing.Gradients"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Gradients" Height="587" Width="347">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Rectangle Width="150" Height="100" Margin="5">
            <Rectangle.Fill>
                <LinearGradientBrush >
                    <GradientStop Color="Blue" Offset="0"/>
                    <GradientStop Color="White" Offset="1" />
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="5">Diagonal Linear Gradient</TextBlock>

        <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>
        <TextBlock Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Margin="5">With 0.5 Offset for White</TextBlock>

        <Rectangle Width="150" Height="100" Grid.Row="2" Margin="5">
            <Rectangle.Fill>
                <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
                    <GradientStop Color="Blue" Offset="0"/>
                    <GradientStop Color="White" Offset="1" />
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Margin="5">Horizontal Linear Gradient</TextBlock>

        <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>
        <TextBlock Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" Margin="5">Reflected Gradient</TextBlock>

        <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>
        <TextBlock Grid.Row="4" Grid.Column="1" VerticalAlignment="Center" Margin="5">Multicolored Gradient</TextBlock>
    </Grid>
</Window>

Gradients

  渐变画刷并不限于绘制形状。可在使用SolidColorBrush画刷的任何时候替代LinearGradientBrush——例如,填充元素的背景表面(使用Background属性)、填充元素文本的前景色(使用Foreground属性)或者填充边框(使用BorderBrush属性)。如下示例所示:

<Window x:Class="Drawing.GradientText"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="GradientText" Height="300" Width="300">
    <Grid>
        <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>
    </Grid>
</Window>

GradientText

三、RadialGradientBrush画刷

  RadialGradientBrush画刷和LinearGradientBrush画刷的工作方式类似,也使用一系列具有不同偏移值的颜色。与LinearGradientBrush画刷一样,可使用希望的任意多种颜色。区域是放置渐变的方式。

  为指定第一种颜色在渐变中的开始点,需要使用GradientOrigin属性。默认情况下,渐变的开始点是(0.5,0.5),该点表示填充区域的中心。

  渐变从开始点以环形的方式向外辐射。渐变最终到达内部渐变圆的边缘,这里是渐变的终点。根据所期望的效果,渐变圆的中心可能和渐变开始点对齐,也可能和渐变开始点不对齐。超出内部渐变圆的区域以及填充区域的最外侧边缘。使用在RadialGradientBrush.GradientStops集合中定义的最后一种颜色进行纯色填充。

  可使用三个属性设置内部渐变圆的边界:Center、RadiusX和RadiusY。默认情况下,Center属性被设置为(0.5,0.5),该设置将限定圆的中心放在填充区域的中央,并且该点同时也是渐变开始点。

  RadiusX和RadiusY属性 决定了限定圆的尺寸,默认情况下着两个属性都被设置为0.5.这些值可能不够直观,因为他们根据填充区域的对角范围(一条从填充区域的左上角延伸到右下角的假想线的长度)进行度量。这意味着半径0.5定义了一个圆,该圆的半径是对角线长度的一半。如果填充区域为正方形,使用勾股定理可计算出,该长度大约是填充区域宽度(或宽度)的0.7倍。因此,如果用默认设置填充正方形区域,渐变就从中心点开始,并拉伸大约正方形宽度0.7倍的距离到达最外侧边界。

  对于填充圆形形状并创建发光效果,径向渐变是非常好的选择(水平高超的美工人员通过组合使用渐变创建具有光晕效果的按钮)。一种常见技巧是稍微偏移GradientOrigin点,为形状创建深度感。

<Window x:Class="Drawing.RadialGradient"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="RadialGradient" Height="534" Width="480">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <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>
        <TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="5">A Radial Gradient</TextBlock>

        <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>
        <TextBlock Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Margin="5" TextWrapping="Wrap">A Radial Gradient with an Offset Center</TextBlock>

    </Grid>
</Window>

RadialGradient

四、ImageBrush画刷

  可通过ImageBrush画刷使用位图填充区域。可使用最常见的文件类型,包括BMP、PNG、GIF以及JPEG文件。可通过设置ImageSource属性来制定希望使用的图像。例如,下面的画刷使用一幅名为logo.jpg的图像绘制Grid面板的背景,在程序集中作为资源包含了该图像:

<Grid>
        <Grid.Background>
            <ImageBrush  ImageSource="logo.jpg" />
        </Grid.Background>
    </Grid>

  ImageBrush.ImageSource属性和Image元素的Source属性的工作方式相同,这意味着也可以使用指向资源、外部文件或Web站点的URI设置ImageSource属性。也可通过为ImageSource属性提供DrawingImage对象,创建使用由XAML定义的矢量内容的ImageBrush画刷。可通过这种方法降低开销(通过避免使用更耗资源的Shape类的派生类),或使用矢量图像创建平铺模式。

  在该例中,ImageBrush画刷用于绘制单元格的背景。因此,为了适应填充区域,图像会被拉伸。如果Grid面板比图像的原始尺寸大,就会看到改变图像尺寸造成的显示问题(如常见的模糊效果)。如果Grid面板的形状和图像的宽高比不匹配,为了适应Grid面板,图像会变形。

  为控制该行为,可修改ImageBrush.Stretch属性。例如,可将该属性设置为Uniform,从而为了适应缩放图像时保持图像的高宽比,或将该属性设置为None,使用图像的自然尺寸绘制图像(对于这种情况,为适应容器,部分图像可能被裁减掉)。

  如果绘制的图像比填充区域小,图像会根据AlignmentX和AlignmentY属性进行对齐。未填充的区域保持透明。当使用Uniform设置进行缩放,并且填充区域的形状不同时,就会出现这种情况。如果将Stretch属性设置为None,并且填充区域比图像大,也会出现这种情况。

  还可使用Viewbox属性从图像上裁剪有兴趣使用的一小部分。为此,需要指定4个数值以描述希望从源图上裁剪并使用的矩形部分。前连个数值指定矩形开始的左上角,而后两个数值指定矩形的宽度和高度。唯一的问题是Viewbox属性使用的是相对坐标系统,就像渐变画刷使用的坐标系统那样。这一坐标系统将图像上的左上角指定为(0,0),将右下角指定为(1,1)。

  为理解Viewbox属性的工作原理,分析下面的标记:

<ImageBrush ImageSource="logo.jpg" Stretch="Uniform"
    Viewbox="0.4 0.5 0.2 0.2"></ImageBrush>

  现在,Viewbox属性从(0.4,0.5)开始,这差不多是用图像的一半出开始(从技术角度看,X坐标是宽度的0.4倍,Y坐标是高度的0.5倍)。然后伸展矩形以填充一个20%宽度和20%高度的小方块作为整幅图像(从技术角度看,矩形的长度为图像宽度的0.2倍,矩形的高度为图像高度的0.2倍)。根据Stretch、AlignmentX以及AlignmentY属性的设置,被裁剪下来的部分图像会被拉伸或剧中显示。下图显示两个使用不同ImageBrush对象填充自身的矩形。最上面的矩形显示了整幅图像,下面的矩形使用Viewbox放大了图像中的一小部分。这两个矩形都使用了纯黑色的边框。

<Window x:Class="Drawing.ImageBrushes"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ImageBrushes" Height="389.6" Width="419.6">
    <Canvas>
        <Rectangle Canvas.Left="10" Canvas.Top="10"  Width="271" Height="100" Stroke="Black">
            <Rectangle.Fill>
                <ImageBrush ImageSource="logo.jpg" Stretch="Fill"></ImageBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Canvas.Left="10" Canvas.Top="120" Width="200" Height="200" Stroke="Black">
            <Rectangle.Fill>
                <ImageBrush ImageSource="logo.jpg" Stretch="Uniform"
    Viewbox="0.4 0.5 0.2 0.2"></ImageBrush>
            </Rectangle.Fill>
        </Rectangle>
    </Canvas>
</Window>

ImageBrushes

五、平铺的ImageBrush画刷

  除普遍的ImageBrush画刷外,还有其他令人更加激动的内容。可通过在画刷的表面平铺图像来得到一些有趣的效果。

  当平铺图像时,有两种选择:

  •   按比例平铺。填充区域始终具有相同数量的平铺图像。为适应填充区域,平铺的图像会扩展或收缩。
  •   按固定尺寸平铺。平铺图像始终具有相同的尺寸。填充区域的尺寸决定了显示的平铺图像的数量。

  为了平铺一幅图像,需要设置ImageSource属性(指定希望平铺的图像)以及ViewPort、ViewportUnits与TitleMode属性。后三个属性决定了平铺图像的尺寸和排列方式。

  可使用Viewport属性设置每幅平铺图像的尺寸。为使用按比例平铺模式,必须将ViewportUnits属性这是为RelativeToBoundingBox(默认值)。然后使用在两个方向上的坐标范围都是从0到1的按比例坐标定义平铺图像的尺寸。换句话说,如果一幅平铺图像的左上角位于(0,0),右下角位于(1,1),就会占据整个填充区域。为得到平铺模式,为Viewport属性设置的值应当比整个填充区域的尺寸小。如下所示:

<ImageBrush ImageSource="tile.jpg" TileMode="Tile"
                     Viewport="0 0 0.5 0.5"></ImageBrush>

  上面的标记创建了一个从填充区域的左上角(0,0)开始,并拉伸到中间点(0.5,0.5)的Viewport方框。因此,不管填充区域的大小如何,填充区域始终包含4幅平铺图像。这样行为非常好,因为可确保平铺图像不会在形状的边缘被裁减(当然,如果使用ImageBrush画刷填充非矩形区域,图像仍会被裁剪)。

  因为这个示例中的平铺图像采用相对于填充区域的尺寸,所以更大的填充区域会使用更大的平铺图像,并且因为改变了图像的尺寸。所以会造成一定的模糊效果。此外,如果填充区域不是完美的正方形,相对坐标系统会相应地进行行挤压,从而每个平铺的正方形都会变成矩形。

  可通过修改Stretch属性(默认设置为Fill)改变这种行为。如果将该属性设置为None,可保证平铺图像永不变形,并且保持正确的形状,然而,如果填充区域不是正方形,将在平铺图像之间显示空白空间。

  第三种选择是将Stretch属性设置为UniformToFill这种设置会根据需要裁减平铺的图像。使用这种方式,平铺图像会保持正确的纵横比,而且平铺的图像之间没有空白空间。然而,如果填充的区域不是正方形,就不会看到完整的平铺图像。

  自动改变平铺图像的尺寸是一项非常有用的功能,但也是需要付出代价。有些位图可能不能正确地改变其尺寸。在某种程度上,可通过提供比所需位图更大的位图,为应对这种情况做好准备。当当缩小图像时,这种技术就会导致更模糊的位图。

  另一种定义平铺图像的尺寸的方法是根据原始图像的尺寸使用绝对坐标。为此,将ViewportUnits属性设置为Absolute。下面举一个示例,该例将每幅平铺图像定义为32X32单位大小,并从左上角开始平铺:

<ImageBrush ImageSource="tile.jpg" TileMode="Tile"
                            ViewboxUnits="Absolute" Viewbox="0 0 32 32"/>

  这种模式的缺点就是填充区域的高度和宽度必须能被32整除。否则,在填充区域便于就会显示部分平铺图像。如果使用ImageBrush画刷填充可改变尺寸的元素,就无法避免该问题,所以必须接受平铺图像未必能与填充区域的边缘对齐这种情况。

  下面是上面示例的完整XAML:

<Window x:Class="Drawing.TileTypes"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TileTypes" Height="500" Width="296.8">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <TextBlock Margin="3">固定尺寸<LineBreak></LineBreak>平铺</TextBlock>
        <Rectangle Grid.Column="1" Stroke="Black">
            <Rectangle.Fill>
                <ImageBrush ImageSource="tile.jpg" TileMode="Tile"
                            ViewboxUnits="Absolute" Viewbox="0 0 32 32"/>
            </Rectangle.Fill>
        </Rectangle>

        <TextBlock Grid.Row="1" Margin="3">按比例<LineBreak></LineBreak>平铺</TextBlock>
        <Rectangle Grid.Row="1" Grid.Column="1" Stroke="Black">
            <Rectangle.Fill>
                <ImageBrush ImageSource="tile.jpg" TileMode="Tile"
                     Viewport="0 0 0.5 0.5"></ImageBrush>
            </Rectangle.Fill>
        </Rectangle>

        <TextBlock Grid.Row="2" Margin="3">
      按比例<LineBreak></LineBreak>平铺(无拉伸)
        </TextBlock>
        <Rectangle Grid.Row="2" Grid.Column="1" Stroke="Black">
            <Rectangle.Fill>
                <ImageBrush ImageSource="tile.jpg" TileMode="Tile" Stretch="None"
                    Viewport="0 0 0.5 0.5"></ImageBrush>
            </Rectangle.Fill>
        </Rectangle>
    </Grid>
</Window>

TileTypes

  下表列出了TileMode枚举值得所有选项.

表 TileMode枚举值

  如果需要 使平铺图像更无缝地混合,翻转行为通常是有用的。例如,如果使用FlipX,相邻的平铺图像总可以无缝地排列。下面举例说明一下。

<Window x:Class="Drawing.TileFlip"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TileFlip" Height="300" Width="300">
    <!-- Overlay labels by putting one UniformGrid on top of another. -->
    <Grid>
        <UniformGrid>
            <Rectangle Stroke="Black">
                <Rectangle.Fill>
                    <ImageBrush ImageSource="tile.jpg" TileMode="Tile"
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Stroke="Black">
                <Rectangle.Fill>
                    <ImageBrush ImageSource="tile.jpg" TileMode="FlipX"
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Stroke="Black">
                <Rectangle.Fill>
                    <ImageBrush ImageSource="tile.jpg" TileMode="FlipY"
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Stroke="Black">
                <Rectangle.Fill>
                    <ImageBrush ImageSource="tile.jpg" TileMode="FlipXY"
                    ViewportUnits="Absolute" Viewport="0 0 37 37"></ImageBrush>
                </Rectangle.Fill>
            </Rectangle>
        </UniformGrid>
        <UniformGrid>
            <UniformGrid.Resources>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="HorizontalAlignment" Value="Center"></Setter>
                    <Setter Property="VerticalAlignment" Value="Bottom"></Setter>
                    <Setter Property="FontSize" Value="25"></Setter>
                    <Setter Property="FontWeight" Value="Bold"></Setter>
                    <Setter Property="Margin" Value="3"></Setter>
                </Style>
            </UniformGrid.Resources>
            <TextBlock>Tile</TextBlock>
            <TextBlock>FlipX</TextBlock>
            <TextBlock>FlipY</TextBlock>
            <TextBlock>FlipXY</TextBlock>
        </UniformGrid>
    </Grid>
</Window>

TileFlip

 六、VisualBrush画刷

  VisualBrush画刷不常用,使用这种画刷获取元素的可视化内容,并使用该内容填充任意表面。例如,可使用VisualBrush画刷将窗口中某个按钮的外观复制到同一个窗口中的其他位置。然而,复制的按钮不能被单击,也不能通过任何方式与其进行交互。在此就复制了元素的外观。例如,下面的标记片段定义了一个按钮和用于复制该按钮的VisualBrush画刷:

<Button Name="cmd" Margin="3" Padding="5">Is this a real button?</Button>
        <Rectangle Margin="3" Height="{Binding ElementName=cmd,Path=ActualHeight}">
            <Rectangle.Fill>
                <VisualBrush Visual="{Binding ElementName=cmd}"></VisualBrush>
            </Rectangle.Fill>
</Rectangle>

  尽管可在VisualBrush本身定义希望使用的元素,但通常使用绑定表达式引用当前窗口中的额元素。如本例所示,下图显示了原始按钮(在窗口顶部)和几个形状不同的区域,这些区域是用基于按钮的VisualBrush画刷绘制的。完整XAML如下所示:

<Window x:Class="Drawing.VisualBrush"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="VisualBrush" Height="300" Width="300">
    <StackPanel Margin="3">
        <Button Name="cmd" Margin="3" Padding="5">Is this a real button?</Button>
        <Rectangle Margin="3" Height="{Binding ElementName=cmd,Path=ActualHeight}">
            <Rectangle.Fill>
                <VisualBrush Visual="{Binding ElementName=cmd}"></VisualBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Margin="3" Height="50">
            <Rectangle.Fill>
                <VisualBrush Visual="{Binding ElementName=cmd}"></VisualBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Margin="3" Height="150">
            <Rectangle.Fill>
                <VisualBrush Visual="{Binding ElementName=cmd}"></VisualBrush>
            </Rectangle.Fill>
        </Rectangle>
    </StackPanel>
</Window>

VisualBrush

  VisualBrush监视元素外观的变化。例如,如果复制某个按钮的可视化外观,而且此外按钮收到焦点,VisualBrush画刷会使用新的可视化内容重新绘制填充区域——一个具有焦点的按钮。VisualBrush类继承自TileBrush类,因此,VisualBrush类也支持所有的裁剪、拉伸以及翻转等特性。

七、BitmapCacheBrush画刷

  BitmapCacheBrush画刷在许多方面和VisualBrush画刷类似。尽管VisualBrush类提供了用于引用其他元素的Visual属性,但BitmapCacheBrush类提供了与此作用相同的Target属性。

  两者之间的关键区别是,BitmapCacheBrush画刷采用可视化内容(这些内容以及通过变换、裁剪、效果以及透明设置进行了改变)并要求显卡在显存中存储该内容。这样一来,当需要时可快速地重新绘制内容,而不必要求WPF执行任何额外的工作。

  为配置位图缓存,设置BitmapCacheBrush.BitmapCache属性(使用可预先确定的BitmapCache对象)。下面是最简单的用法:

<Button Name="cmd" Margin="3" Padding="5">Is this a real button?</Button>
        <Rectangle Margin="3" Height="{Binding ElementName=cmd,Path=ActualHeight}">
            <Rectangle.Fill>
                <BitmapCacheBrush Target="{Binding ElementName=cmd}">
                </BitmapCacheBrush>
            </Rectangle.Fill>
        </Rectangle>

  BitmapCacheBrush画刷存在严重缺点:渲染位图以及将其复制到显存的初始化步骤需要比较短但可察觉到得额外时间。如果在窗口中使用BitmapCacheBrush画刷,在窗口第一次绘制自身之前,当渲染BitmapCacheBrush并复制其位图时,将会注意到延迟。因此,在传统窗口中,BitmapCacheBrush起不到多大的帮助作用。

  然而,如果在用户界面中大量使用动画,值得考虑使用位图缓存。这是因为动画会强制窗口在没一秒内重新绘制多次。如果具有复杂的矢量内容,从缓存位图中绘制窗口内容比从头重新绘制窗口要快。但即使是这种情况,也不应当立即使用BitmapCacheBrush画刷。可能更愿意通过为每个希望缓存的元素设置更高级的UIElement.CacheMode属性来应用缓存。对于这种情况,WPF在后台使用BitmapCacheBrush画刷获取相同的效果,但需要做的工作更少。

  根据这些细节,BitmapCacheBrush画刷本身好像不是很有用。然而,如果需要在几个地方绘制单块复杂的可视化内容,使用BitmapCacheBrush画刷是合理的。对于这种情况,通过使用BitmapCacheBrush画刷缓存整个可视化内容比单独缓存每个元素更节省内存。在此输出,这种节省可能得不偿失,除非用户界面还使用了动画。

(0)

相关推荐