WPF程序设计 :第二章 基本画刷(Basic Brushes)

时间:2023-02-19 20:25:11

1.Color、Colors

(1) Color(structure):

WPF的颜色被封装成Color结构(structure),定义在System.Window.Media命名空间中。

 

Color clr  =   new  Color;
clr.A 
=   255 ;
clr.R 
=   255 ;
clr.G 
=   0 ;
clr.B 
=   255 ;

 

如上代码,除了三原色(R、G、B)外,Color结构还有一个"alpha chanel",其property名称为 A。用来控制颜色不透明的。(0 - 255,完全透明 - 不透明)。

 

(2)Colors:

和Color在同一命名空间,包含有141个静态只读property,他们大都是好记的颜色引用方式,注意的是:其alpha 都是 255;

Color clr  =  Colors.Blue;

 

实例程序:

见第 2 节中。

 

2.SolidBrushColor、Brushes

(1)SolidBrushColor

最简单的画刷,单色画刷。

Color clr  =  Color.FromRgb( 0 255 255 );
SolidBrushColor brush 
= new  SolidBrushColor(clr);
Background 
=  brush; 

 

用一行代码书写:

 

WPF程序设计 :第二章 基本画刷(Basic Brushes)WPF程序设计 :第二章 基本画刷(Basic Brushes)Code

 

实例程序:

 

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Windows;
using  System.Windows.Media;

namespace  Jack.VaryTheBackground
{
    
public   class  VaryTheBackground : Window
    {
        SolidColorBrush brush 
=   new  SolidColorBrush(Colors.Black);

        [STAThread]
        
public   static   void  Main()
        {
            Application app 
=   new  Application();
            app.Run(
new  VaryTheBackground());
        }

        
public  VaryTheBackground()
        {
            Title 
=   "  Vary the Back Ground " ;
            Width 
=   384 ;
            Height 
=   384 ;
            Background 
=  brush;
        }

        
protected   override   void  OnMouseMove(System.Windows.Input.MouseEventArgs e)
        {
            
double  width  =  ActualWidth 
                
-   2   *  SystemParameters.ResizeFrameVerticalBorderWidth;

            
double  height  =  ActualHeight  -   2   *  SystemParameters.ResizeFrameHorizontalBorderHeight  -  SystemParameters.CaptionHeight;

            Point ptMouse 
=  e.GetPosition( this );
            Point ptCenter 
=   new  Point(width  /   2 , height  /   2 );

            Vector vectMouse 
=  ptMouse  -  ptCenter;
            
double  angle  =  Math.Atan2(vectMouse.Y, vectMouse.X);
            Vector vecEllipse 
=   new  Vector(width  /   2   *  Math.Cos(angle), height  /   2   *  Math.Sin(angle));

            Byte byLevel 
=  ( byte )( 255   *  ( 1   -  Math.Min( 1 , vectMouse.Length  /  vecEllipse.Length)));
            Color clr 
=  brush.Color;
            clr.R 
=  clr.G  =  clr.B  =  byLevel;
            brush.Color 
=  clr;
        }
    }
}

 

(2) Brushes

正如Colors类提供的141个静态只读的property一样,Brushes(也是复数)类也提供了141个静态只读的property,名称和Color的property都一样,但是Brushes的property返回的是SolidBrushColor对象。

 

this .Background  =  Brushes.Red;

等价于:

this .Background  =   new  SolidColorBrush(Colors.Red);

 

这两种方法均可以把窗口填上特定的颜色,但是这两种做法其实是存在一定差异的,想上面的实力程序VaryBackground这样的程序就可以感受这个差异。

 

Background  =   new  SolidColorBrush(Colors.Black);

写成:

Background  =  Brushes.Black;

 

重新编译,并执行,鼠标经过Window窗体时,将会弹出一个" Invalid Operation Exception"(无效操作异常)消息框,详细信息是"无法设定'#FF000000' 对象的property,因为此对象为只读状态"。问题发生在OnMouseMove方法的最后一条语句(brush.Color = clr;)这里是试图重新设定画刷的Color的property。(异常信息的单引号中出现的十六进制数,是当前Color property的值。)

 

分析:"Brushes.Black" , 利用的Brushes所取得的这个SolidColorBrush对象是处于冻结(frozen)状态。也就是说,不能再被改变。就像Changed事件一样,Freezeable实现了冻结,而Brush的冻结正是从这里继承而来的。如果Freezeable对象的CanFreeze property是true,可以调用Freeze方法来实现对象的冻结和不可变动。IsFrozen property如果变成了true,就表示(对象)已经被冻结了。将对象冻结可以提高效率,因为被冻结的对象不会被改变了,所以不需监控。冻结的Freezable对象还可以在不同的线程之间共享,没有被冻结的Freezable对象则不行。虽然,无法将冻结对象解冻,但是你可以做出一个没有冻结的复制版本。

下面 版本可以定义VaryTheBackground中的brush字段(field):

 

SolidColorBrush brush  =  Brushes.Black.Clone();

 

错误将消失。

 

如果你想看到这141个画刷出现在同一个窗口的客户去。FlipThroughTheBrush 程序可以达成你的愿望,你可以用上下箭头(Up 上;Down下)来改变画刷。

请看程序:

 

using  System;
using  System.Text;
using  System.Media;
using  System.Windows;
using  System.Reflection;
using  System.Windows.Media;
using  System.Windows.Input;

namespace  Jack.PartTwo
{
    
public   class  FlipThroughTheBrushes : Window
    {
        
int  index  =   0 ;
        PropertyInfo[] props;

        
public  FlipThroughTheBrushes()
        {
            props 
=   typeof (Brushes).GetProperties(BindingFlags.Static  |  BindingFlags.Public); 
                                                     
//  使用reflaction(反射)取得Brushes的所有成员,
                                                     
//  调用GetProperties时,以BinddingFlags为参数,
                                                    
//  程序就可以清楚地把自己限制在Brushes公开和静态的property上。
            SetTitleAndBackGround();
        }

        [STAThread]
        
public   static   void  Main()
        {
            Application app 
=   new  Application();
            app.Run(
new  FlipThroughTheBrushes()); 
        }

        
protected   override   void  OnKeyDown(KeyEventArgs e)
        {
            
if  (e.Key  ==  Key.Down  ||  e.Key  ==  Key.Up)
            {
                index 
+=  e.Key  ==  Key.Up  ?   1  : props.Length  -   1 ;
                index 
%=  props.Length;

                SetTitleAndBackGround();
            }
            
base .OnKeyDown(e);
        }

        
//  被调方法,将Title property和Background property设定为Brushes类的某个成员
         private   void  SetTitleAndBackGround() 
        {
            
//  "props[0].Name"会返回一个字符串,这是类第一个property
            Title  =   " Flip Through the Brushes -  "   +  props[index].Name;

            Background 
=  (Brush)props[index].GetValue( null null );
        }
    }
}

说明的是:props[0].GetValue(null,null); 返回实际的SolidColorBrush对象,这里的GetValue方法需要两个null参数;通常第一个参数是property所在的对象,因为Brushes是一个静态的property,所以没有对应的对象,因此传入null;第二个参数只有在property是indexer(索引器)时才必要。

 

3.GradientBrush(Abstract)(1.LinearGradientBrush、2.RadialGradientBrush)

渐变画刷的使用:

(1)LinearGradientBrush

这个是最简单的渐变画刷形式,只需要两个Color(如 clr1 和 clr2)对象,和两个Point(pt1 和 pt2)对象。pt1位置的颜色是clr1,pt2位置的颜色是clr2.在两个位置的连线上,则是混合了两种颜色,即一种渐变的效果,连线的中心则就是两种颜色的平均值。垂直于连线的位置,和连线上的点使用相同的颜色。至于操过pt1和pt2的两边的颜色是什么颜色,以后待说。

实例程序:

 

WPF程序设计 :第二章 基本画刷(Basic Brushes)WPF程序设计 :第二章 基本画刷(Basic Brushes)Code

效果见下图:

WPF程序设计 :第二章 基本画刷(Basic Brushes)

如果,上面实例代码中(1,1 )改成(0.25,0.25)效果如下:

WPF程序设计 :第二章 基本画刷(Basic Brushes)

你会发现窗口右下角有一大块超出(0.25,0.25)这点之外。默认情况下,这里会着上第二种颜色。这受SpreadMethod property属性的控制。此property类型是GradientSPreadMethod枚举,默认是Pad,表示超出的部分延续之前的颜色不再变化;除了Pad,还有Reflact 和 Repeat。你可以试着把GradientTheBrush程序修改成下面这样:

 

LinearGradientBrush brush  =  
    
new  LinearGradientBrush(Colors.Red, Colors.Blue,  new  Point( 0 0 ),  new  Point( 0.25 0.25 ));

brush.SpreadMethod 
=  GradientSpreadMethod.Reflect; 

 

WPF程序设计 :第二章 基本画刷(Basic Brushes)

可见,(0,0)和(0.25,0.25)之间,画刷从红到蓝渐变;然后再(0.25,0.25)和(0.5,0.5)之间,从蓝到红;接着在(0.5,0.5)和(0.75,0.75)之间,从红到蓝渐变,在(0.75,0.75)和(1,1)之间,从蓝到红渐变。

 

 

看下面这个实例程序,据窗体计算对角线垂直变量(有详细注释):

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;

namespace Jack.PartTwo
{
    
public class AdjustTheGradient : Window
    {
        LinearGradientBrush brush;
        
        [STAThread]
        
public static void Main()
        {
            Application app 
= new Application();
            app.Run(
new AdjustTheGradient()); // 设置你的启动程序类(该类理解为继承自Window的窗体)
        }

        
public AdjustTheGradient()
        {
            Title 
= "Adjust the Gradient";
            
this.SizeChanged += new SizeChangedEventHandler(AdjustTheGradient_SizeChanged);

            brush 
= new LinearGradientBrush(Colors.Red, Colors.Blue, 0);
            brush.MappingMode 
= BrushMappingMode.Absolute; // BrushMappingMode枚举类型,只有两个值:
                                                           
// 1.RelativeToBoundBox : 使用相对坐标默认值(左上角顶点为(0,0),中间点为(0.5,0.5))
                                                           
// 2.Absolute : 与设备无关
            Background = brush;
        }

        
void AdjustTheGradient_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            
// 实际工作去的宽度
            double width = ActualWidth
                
- 2 * SystemParameters.ResizeFrameVerticalBorderWidth;   // 左右两边的厚度

            
// 实际工作区的高度
            double height = ActualHeight
                
- 2 * SystemParameters.ResizeFrameHorizontalBorderHeight // 上下两边的厚度
                - SystemParameters.CaptionHeight;       // 窗体非工作区(标题(Title),快捷按钮(×,最大化等)等)

            Point ptCenter 
= new Point(width / 2, height / 2);           // 工作区的中心点位置
            
            
//Vector Define
            Vector vectDiag = new Vector(width, - height);               // 对角线的向量 (从左下到右上)  
                                                        
//等价于vectDiag = new Point(width, 0) - new Point(0,height)

            Vector vectPerp 
= new Vector(vectDiag.Y, -vectDiag.X);        // 该向量垂直于对角线,即 是一个与vectDiag垂直的向量
                                            
// 得到的方法:只要把X和Y的值对调,并把其中一个数的正负号反向就可以了。

            vectPerp.Normalize();    
// 规格化操作,将该向量的X和Y分别除以Leght,是该向量的Length变成1。

            vectPerp 
*= width * height / vectDiag.Length;  

                                            // Vector.Length 指的是这个向量的量,即开始点和终点的距离

            brush.StartPoint 
= ptCenter + vectPerp;
            brush.EndPoint 
= ptCenter - vectPerp;
        }
    }
}

 

(2) RadialGradientBrush

基础概念:

RadialGradientBrush的许多property都已经具备实用的默认值。其中三个property用来定义一个椭圆:Center是Point类型,定义为(0.5,0.5),也就是画刷涵盖区域的中心点。RadiusX以及RadiusY的property皆为double类型,分别代表椭圆的水平和垂直轴半径,默认值是0.5,所以,无论是横向还是纵向,椭圆都达到了挡墙画刷作用域 的边界。

 

椭圆的圆周受到Center、RadiusX、RadiusY 三个property的影响,圆周的颜色,正是Offset property为1时的颜色。

 

还有个名为GradientOrigin的property,和Center一样,是Point对象,默认值为(0.5,0.5)。顾名思义,GradientOrigin是渐变开始的原点。在这个点,你会看到Offset property为0时的颜色。

 

在GradientOrigin和椭圆圆周之间,就是发生渐变的地方。下面是几个实例程序:

 

(1)一系列的同心渐变圆圈

 

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Windows;
using  System.Windows.Media;
using  System.Windows.Input;

namespace  Jack.PartTwo
{
    
public   class  ClickTheGradientCenter : Window
    {
        RadialGradientBrush brush;

        [STAThread]
        
public   static   void  Main()
        {
            Application app 
=   new  Application();
            app.Run(
new  ClickTheGradientCenter());  //  设置你的启动程序类(该类理解为继承自Window的窗体)
        }
        
public  ClickTheGradientCenter()
        {
            Title 
=   "  Click the Gradient Brush " ;
            brush 
=   new  RadialGradientBrush(Colors.White, Colors.Red);
            brush.RadiusX 
=  brush.RadiusY  =   0.20 ;
            
// brush.SpreadMethod = GradientSpreadMethod.Repeat;
            Background  =  brush;
        }

        
protected   override   void  OnMouseDown(MouseButtonEventArgs e)
        {
            
double  width  =  ActualWidth  -   2   *  SystemParameters.ResizeFrameVerticalBorderWidth;
            
double  height  =  ActualHeight  -   2   *  SystemParameters.ResizeFrameHorizontalBorderHeight  -  SystemParameters.CaptionHeight;

            Point ptMouse 
=  e.GetPosition( this );
            ptMouse.X 
/=  width;
            ptMouse.Y 
/=  height;

            
if  (e.ChangedButton  ==  MouseButton.Left)
            {
                brush.Center 
=  ptMouse;
                brush.GradientOrigin 
=  ptMouse;
            }
            
            
if (e.ChangedButton  ==  MouseButton.Right)
            {
                brush.GradientOrigin 
=  ptMouse;
            }
        }
    }
}

 

(2)动画效果:

 

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Windows;
using  System.Windows.Media;
using  System.Windows.Threading;

namespace  Jack.PartTwo
{
    
public   class  RotateTheGradientOrigin : Window
    {
        RadialGradientBrush brush;
        
double  angle;

        [STAThread]
        
public   static   void  Main()
        {
            Application app 
=   new  Application();
            app.Run(
new  RotateTheGradientOrigin());  //  设置你的启动程序类(该类理解为继承自Window的窗体)
        }

        
public  RotateTheGradientOrigin()
        {
            Title 
=   " Rotate the Gradient Origin " ;
            WindowStartupLocation 
=  WindowStartupLocation.CenterScreen;
            Width 
=   384 //  相当于4英寸
            Height  =   384 ;

            brush 
=   new  RadialGradientBrush(Colors.White, Colors.Blue);
            brush.Center 
=  brush.GradientOrigin  =   new  Point( 0.5 0.5 );
            brush.RadiusX 
=  brush.RadiusY  =   0.10 ;
            brush.SpreadMethod 
=  GradientSpreadMethod.Repeat;

            Background 
=  brush;

            DispatcherTimer tmr 
=   new  DispatcherTimer();
            tmr.Interval 
=  TimeSpan.FromMilliseconds( 100 );
            tmr.Tick 
+=   new  EventHandler(tmr_Tick);
            tmr.Start();
        }

        
void  tmr_Tick( object  sender, EventArgs e)
        {
            Point pt 
=   new  Point( 0.5   +   0.5   *  Math.Cos(angle),  0.5   +   0.05   *  Math.Sin(angle));
            brush.GradientOrigin 
=  pt;
            angle 
+=  Math.PI  /   6 ;
        }
    }
}

 

我们这章是来学习和WPF基本画刷的使用和一些技术点,将焦点放在Window的Background property,但是Window还有另外三个Property也是Brush类型的。一个是OpacityMask,这个property是从UIElement继承而来的,后面讨论位图(bitmap)时再对其做较多介绍了;另外两个Brush property都是从Control继承而来的。一个是BorderBrush,可以在客户区的周边绘制一个边框;另一个是Foreground。