跟小D每日学口语

基本画刷2

第01个小程序:随鼠标移动而变色的背景(VaryTheBackground.cs)

程序运行时,当鼠标指针靠近窗口中心程度不同,客户区的背景颜色也不同。

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. namespace Chapter02
  6. {
  7.     public class VaryTheBackground : Window
  8.     {
  9.         SolidColorBrush brush = new SolidColorBrush(Colors.Black);  //创建画刷 。SolidColorBrush是最简单的画刷。
  10.         [STAThread]
  11.         public static void Main()
  12.         {
  13.             Application app = new Application();
  14.             app.Run(new VaryTheBackground());
  15.         }
  16.         public VaryTheBackground()
  17.         {
  18.             Title = "Vary the Background";
  19.             Width = 384;
  20.             Height = 384;
  21.             Background = brush;
  22.         }
  23.         //鼠标移动事件
  24.         protected override void OnMouseMove(MouseEventArgs args)
  25.         {
  26.             //窗口呈现尺寸-边框和标题栏的尺寸
  27.             double width = ActualWidth
  28.                 - 2 * SystemParameters.ResizeFrameVerticalBorderWidth;
  29.             double height = ActualHeight
  30.                 - 2 * SystemParameters.ResizeFrameHorizontalBorderHeight
  31.                 - SystemParameters.CaptionHeight;
  32.             Point ptMouse = args.GetPosition(this);  //获取鼠标位置
  33.             Point ptCenter = new Point(width / 2, height / 2); //窗口中心
  34.             Vector vectMouse = ptMouse - ptCenter;     //向量。向:中心指向鼠标位置;量:鼠标位置与中心距离
  35.             double angle = Math.Atan2(vectMouse.Y, vectMouse.X);
  36.             Vector vectEllipse = new Vector(width/ 2 * Math.Cos(angle),
  37.                                             height / 2 * Math.Sin(angle));
  38.             Byte byLevel = (byte)(255 * (1 - Math.Min(1, vectMouse.Length / vectEllipse.Length)));
  39.             Color clr = brush.Color;
  40.             clr.R = clr.G = clr.B = byLevel;
  41.             brush.Color = clr;
  42.         }
  43.     }
  44. }

WPF的颜色被封装成Color结构,定义在System.Windows.Media命名空间中;和一般的图形环境一样,Color结构使用红、绿、蓝三原色来表达颜色。这三种原色分别被简称为R、G、B;用这三种原色定义出来的三维空间,就是RGB颜色空间。

Color结构包含名为R、G、B的三个可读写属性,它们的类型都是byte。这三种属性的值范围从0到255。当这三个Property皆为0时就是黑色,当这三个Property皆为255时就是白色。

如下图,假设绿色矩形区域为屏幕窗口,点ptMouse(鼠标指针)为M点, 点ptCenter(中心)为O点,向量vectMouse 对应图中OM,角度angle为角MOA,向量vectEllipse 对应图中OM'。显然,向量OM'的长度大于等于向量OM的长,对应vectEllipse.Length>=vectMouse.Length ,当鼠标在屏幕边界时,OM=OM',byLevel =255*0,clr.R = clr.G = clr.B =0,屏幕为黑色;当鼠标在屏幕中心时,OM长为0,byLevel =255*1,clr.R = clr.G = clr.B =255,屏幕显示为白色。

只要鼠标在程序客户区,OnMouseMove方法会被持续调用。

每次只要brush一有改变,客户区域就会被重绘,这些都是幕后进行的。这里之所以会有动态的瓜,是因为Brush类继承自Freezable类,而Freezable类实现了一个名为Change的事件,Brush对象只要一有改变,这个事件就会被触发,背景就会被重画。

第02个小程序:遍历画笔(FlipThroughTheBrushes.cs)

  1. using System;
  2. using System.Reflection;
  3. using System.Windows;
  4. using System.Windows.Input;
  5. using System.Windows.Media;
  6. namespace Chapter02
  7. {
  8.     public class FlipThroughTheBrushes : Window
  9.     {
  10.         int index = 0;         //索引值
  11.         PropertyInfo[] props;  //属性数组
  12.         [STAThread]
  13.         public static void Main()
  14.         {
  15.             Application app = new Application();
  16.             app.Run(new FlipThroughTheBrushes());
  17.         }
  18.         public FlipThroughTheBrushes()
  19.         {
  20.             //取得Brushes类的成员,放在props中
  21.             props = typeof(Brushes).GetProperties(BindingFlags.Public | BindingFlags.Static);
  22.             SetTitleAndBackground();
  23.         }
  24.         //鼠标按下时
  25.         protected override void OnKeyDown(KeyEventArgs args)
  26.         {
  27.             if (args.Key == Key.Down || args.Key == Key.Up)
  28.             {
  29.                 //Key.Down时index-1,Key.UP时index+1
  30.                 index += args.Key == Key.Up ? 1 : props.Length - 1;
  31.                 //index对属性数组长度取余,防止索引越界
  32.                 index %= props.Length;
  33.                 SetTitleAndBackground();
  34.             }
  35.             base.OnKeyDown(args);
  36.         }
  37.         //设置背景
  38.         void SetTitleAndBackground()
  39.         {
  40.             Title = "Flip Through the Brushes - " + props[index].Name;
  41.             Background = (Brush)props[index].GetValue(nullnull);  //设置背景画笔
  42.         }
  43.     }
  44. }

GetProperties()函数用于返回对象数组,参数是限制Brushes公开和静态的属性。其实这里可以不需要这样的限制,因为Brushes的属性本来全部都是public和static。

props[index].Name返回第index个属性的名称;props[index].GetValue(nullnull)返回实际的SolidColorBrush对象,第一个参数是属性所在的对象,因为Brushes是一个静态属性,没有对应的对象,传入null;第二个参数只有在属性是索引器是才有必要。

第03个小程序:渐变画笔(GradiateTheBrush.cs)

渐变画刷的实现

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. namespace Chapter02
  6. {
  7.     public class GradiateTheBrush : Window
  8.     {
  9.         [STAThread]
  10.         public static void Main()
  11.         {
  12.             Application app = new Application();
  13.             app.Run(new GradiateTheBrush());
  14.         }
  15.         public GradiateTheBrush()
  16.         {
  17.             Title = "Gradiate the Brush";
  18.             //渐变画刷。左上角(0,0)红色,右下角(1,1)蓝色,中间渐变色。
  19.             LinearGradientBrush brush =
  20.                 new LinearGradientBrush(Colors.Red, Colors.Blue,
  21.                                         new Point(0, 0),new Point(1, 1));
  22.             Background = brush;
  23.         }
  24.     }
  25. }

这里的表面被视为一个单位宽,一个单位高。表面的左上角是(0,0),右下角是(1,1)。

brush有个SpreadMethod属性,其默认值是GradientSpreadMethod.Pad,表示超出设定区域部分延续之前的颜色;另一个值GradientSpreadMethod.Reflect,表示按相向方向重复渐变;还有一个值GradientSpreadMethod.Repeat,表示按原始方向重复渐变。

修改LinearGradientBrush brush =……代码如下:

  1.             LinearGradientBrush brush =
  2.                 new LinearGradientBrush(Colors.Red, Colors.Blue,
  3.                                         new Point(0, 0),new Point(0.25, 0.25));
  4.             brush.SpreadMethod = GradientSpreadMethod.Pad;
  5.             //brush.SpreadMethod = GradientSpreadMethod.Reflect;
  6.             //brush.SpreadMethod = GradientSpreadMethod.Repeat;

SpreadMethod属性取不同值时,效果分别如下

第04个小程序:调整(设置)渐变参数(AdjustTheGradient.cs)

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. namespace Chapter02
  6. {
  7.     class AdjustTheGradient : Window
  8.     {
  9.         LinearGradientBrush brush;
  10.         [STAThread]
  11.         public static void Main()
  12.         {
  13.             Application app = new Application();
  14.             app.Run(new AdjustTheGradient());
  15.         }
  16.         public AdjustTheGradient()
  17.         {
  18.             Title = "Adjust the Gradient";
  19.             //事件处理器
  20.             SizeChanged += WindowOnSizeChanged;
  21.             //画笔新实例。第三个参数角度为0表示从起点到终点水平渐变。
  22.             brush = new LinearGradientBrush(Colors.Red, Colors.Blue, 0);
  23.             //指定渐变画笔的定位坐标相对于输出区域是绝对的。
  24.             brush.MappingMode = BrushMappingMode.Absolute;
  25.             //背景画笔
  26.             Background = brush;
  27.         }
  28.         //窗口大小改变引发事件
  29.         void WindowOnSizeChanged(object sender, SizeChangedEventArgs args)
  30.         {
  31.             //获取窗口区域(没有边界,没有标题栏的区域)的宽和高
  32.             double width = ActualWidth
  33.                 - 2 * SystemParameters.ResizeFrameVerticalBorderWidth;
  34.             double height = ActualHeight
  35.                 - 2 * SystemParameters.ResizeFrameHorizontalBorderHeight
  36.                 - SystemParameters.CaptionHeight;
  37.             
  38.             //中心
  39.             Point ptCenter = new Point(width / 2,height / 2);
  40.             //窗口对角线(左下到右上)向量,即 = new Point(width, 0)  new Point(0, height);
  41.             Vector vectDiag = new Vector(width,-height);
  42.             //垂直于对角线的向量
  43.             Vector vectPerp = new Vector(vectDiag.Y, -vectDiag.X);
  44.             //向量规范化,使其长为单位1。
  45.             vectPerp.Normalize();
  46.             //中心到起点的向量
  47.             vectPerp *= width * height / vectDiag.Length;
  48.             //获取渐变的二维起点和终点
  49.             brush.StartPoint = ptCenter + vectPerp;
  50.             brush.EndPoint = ptCenter - vectPerp;
  51.         }
  52.     }
  53. }

本程序实现的渐变方式如下图所示。假设绿色矩形区域为窗口区域,O点相当于中心ptCenter,对角线BD相当于向量vectDiag ,线MN对应于向量vectPerp ,width * height / vectDiag.Length的值等于MO或ON,M、N点对应brush.StartPoint 、brush.EndPoint 。AM、BD、NC都是等色线。

第05个小程序:水平彩虹(FollowTheRainbow.cs)

LinearGradientBrush渐变的颜色可以限定更多。下面水平渐变画彩虹的7种颜色。

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. namespace Chapter02
  6. {
  7.     class FollowTheRainbow : Window
  8.     {
  9.         [STAThread]
  10.         public static void Main()
  11.         {
  12.             Application app = new Application();
  13.             app.Run(new FollowTheRainbow());
  14.         }
  15.         public FollowTheRainbow()
  16.         {
  17.             Title = "Follow the Rainbow";
  18.             LinearGradientBrush brush = new LinearGradientBrush();
  19.             brush.StartPoint = new Point(0, 0);
  20.             brush.EndPoint = new Point(1, 0);
  21.             Background = brush;
  22.             // 设置画笔渐变停止点
  23.             brush.GradientStops.Add(new GradientStop(Colors.Red, 0));
  24.             brush.GradientStops.Add(new GradientStop(Colors.Orange, .17));
  25.             brush.GradientStops.Add(new GradientStop(Colors.Yellow, .33));
  26.             brush.GradientStops.Add(new GradientStop(Colors.Green, .5));
  27.             brush.GradientStops.Add(new GradientStop(Colors.Blue, .67));
  28.             brush.GradientStops.Add(new GradientStop(Colors.Indigo, .84));
  29.             brush.GradientStops.Add(new GradientStop(Colors.Violet, 1));
  30.         }
  31.     }
  32. }

GradientStops属性是GradientStopCollection类型,这是GradientStop对象的集合。

GradientStop(Color color, Double offset),offset值范围在0和1之间,是StartPoint和EndPoint的相对距离。 

效果图:

第06个小程序:圆形彩虹(CircleTheRainbow.cs)

由客户中心向外画彩虹的7种颜色。

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. namespace Chapter02
  6. {
  7.     public class CircleTheRainbow : Window
  8.     {
  9.         [STAThread]
  10.         public static void Main()
  11.         {
  12.             Application app = new Application();
  13.             app.Run(new CircleTheRainbow());
  14.         }
  15.         public CircleTheRainbow()
  16.         {
  17.             Title = "Circle the Rainbow";
  18.             // 定义径向渐变画刷
  19.             RadialGradientBrush brush = new RadialGradientBrush();
  20.             Background = brush;
  21.             // 设置画笔渐变停止点
  22.             brush.GradientStops.Add(new GradientStop(Colors.Red, 0));
  23.             brush.GradientStops.Add(new GradientStop(Colors.Orange, .17));
  24.             brush.GradientStops.Add(new GradientStop(Colors.Yellow, .33));
  25.             brush.GradientStops.Add(new GradientStop(Colors.Green, .5));
  26.             brush.GradientStops.Add(new GradientStop(Colors.Blue, .67));
  27.             brush.GradientStops.Add(new GradientStop(Colors.Indigo, .84));
  28.             brush.GradientStops.Add(new GradientStop(Colors.Violet, 1));
  29.         }
  30.     }
  31. }

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

在椭圆以外的区域(屏幕窗口的四个角落),将继续使用紫色,因为SpreadMethod的默认值是Fill。

本程序效果:

将以上代码作下修改(CircleTheRainbow.cs):

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. using System.Reflection;
  6. namespace Chapter02
  7. {
  8.     public class CircleTheRainbow : Window
  9.     {
  10.         double c = 0;
  11.         [STAThread]
  12.         public static void Main()
  13.         {
  14.             Application app = new Application();
  15.             app.Run(new CircleTheRainbow());
  16.         }
  17.         public CircleTheRainbow()
  18.         {
  19.             Title = "Circle the Rainbow";
  20.             Width = 640;
  21.             Height = 320;
  22.             Draw();
  23.         }
  24.         private void Draw()
  25.         {
  26.             double b = 1.0 / 10;
  27.             // 定义径向渐变画刷
  28.             RadialGradientBrush brush = new RadialGradientBrush();
  29.             brush.RadiusX = 0.8;
  30.             brush.RadiusY = 0.7;
  31.             Background = brush;
  32.             // 设置画笔渐变停止点
  33.             brush.GradientStops.Add(new GradientStop(Colors.Red, (0 + c) % 1));
  34.             brush.GradientStops.Add(new GradientStop(Colors.Orange, (b + c) % 1));
  35.             brush.GradientStops.Add(new GradientStop(Colors.Yellow, (2 * b + c) % 1));
  36.             brush.GradientStops.Add(new GradientStop(Colors.Green, (3 * b + c) % 1));
  37.             brush.GradientStops.Add(new GradientStop(Colors.Blue, (4 * b + c) % 1));
  38.             brush.GradientStops.Add(new GradientStop(Colors.Indigo, (5 * b + c) % 1));
  39.             brush.GradientStops.Add(new GradientStop(Colors.Violet, (6 * b + c) % 1));
  40.             brush.GradientStops.Add(new GradientStop(Colors.YellowGreen, (7 * b + c) % 1));
  41.             brush.GradientStops.Add(new GradientStop(Colors.WhiteSmoke, (8 * b + c) % 1));
  42.             brush.GradientStops.Add(new GradientStop(Colors.Transparent, (9 * b + c) % 1));
  43.             brush.GradientStops.Add(new GradientStop(Colors.RosyBrown, (10 * b + c) % 1));
  44.             
  45.         }
  46.         //按键
  47.         protected override void OnKeyDown(KeyEventArgs args)
  48.         {
  49.             base.OnKeyDown(args);
  50.             //按下空格
  51.             if (args.Key == Key.Space)
  52.             {
  53.                 c += 0.005;
  54.                 Draw();
  55.             }
  56.         } 
  57.     }
  58. }

运行,这回按下空格键时,会看到各椭圆色圈从中心向外扩展,像石子引起的水波一样。

第07个小程序:点击渐变中心(ClickTheRadientCenter .cs)

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. namespace Chapter02
  6. {
  7.     class ClickTheRadientCenter : Window
  8.     {
  9.         RadialGradientBrush brush;
  10.         [STAThread]
  11.         public static void Main()
  12.         {
  13.             Application app = new Application();
  14.             app.Run(new ClickTheRadientCenter());
  15.         }
  16.         public ClickTheRadientCenter()
  17.         {
  18.             Title = "Click the Gradient Center";
  19.             brush = new RadialGradientBrush(Colors.White, Colors.Red);
  20.             //椭圆的水平和垂直半径分别为窗口宽和高的十分之一
  21.             brush.RadiusX = brush.RadiusY = 0.10;
  22.             //渐变绘制方式
  23.             brush.SpreadMethod = GradientSpreadMethod.Repeat;
  24.             Background = brush;
  25.         }
  26.         // 鼠标按下
  27.         protected override void OnMouseDown(MouseButtonEventArgs args)
  28.         {
  29.             //获取窗口区域(没有边界,没有标题栏的区域)的宽和高
  30.             double width = ActualWidth
  31.                 - 2 * SystemParameters.ResizeFrameVerticalBorderWidth;
  32.             double height = ActualHeight
  33.                 - 2 * SystemParameters.ResizeFrameHorizontalBorderHeight
  34.                 - SystemParameters.CaptionHeight;
  35.             Point ptMouse = args.GetPosition(this);
  36.             //因为是采用的是相对坐标系统,鼠标指针位置也要换算成相对(宽和高)的。
  37.             ptMouse.X /= width;
  38.             ptMouse.Y /= height;
  39.             // 鼠标左击
  40.             if (args.ChangedButton == MouseButton.Left)
  41.             {
  42.                 brush.Center = ptMouse;
  43.                 brush.GradientOrigin = ptMouse;
  44.             }
  45.             //鼠标右击
  46.             else if (args.ChangedButton == MouseButton.Right)
  47.                 brush.GradientOrigin = ptMouse;
  48.         }
  49.     }
  50. }

GradientOrigin 是渐变开始的原点,在GradientOrigin 和椭圆圆周之间,就是发生渐变的地方。如果GradientOrigin 等于Center(默认如此),那么渐变会从椭圆圆心到椭圆圆周之间扩散。如果GradientOrigin 和Center之间有一段距离,那么在GradientOrigin 和最接近的椭圆圆周之间,渐变的颜色变化会比较剧烈,相反方向的颜色变化就比较缓和。

本程序就是演示这两种效果。鼠标左击时,GradientOrigin 等于Center,即使它们的值(位置)变了,结果依然是从椭圆中心到边界的渐变;但当鼠标右击时,GradientOrigin 不等于Center,只有GradientOrigin改变,则出现挤压或拉伸的状态。

效果如下:

第08个小程序:旋转渐变原点(RotateTheGradientOrigin .cs)

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. using System.Windows.Threading;
  6. namespace Chapter02
  7. {
  8.     public class RotateTheGradientOrigin : Window
  9.     {
  10.         RadialGradientBrush brush;
  11.         double angle;
  12.         [STAThread]
  13.         public static void Main()
  14.         {
  15.             Application app = new Application();
  16.             app.Run(new RotateTheGradientOrigin());
  17.         }
  18.         public RotateTheGradientOrigin()
  19.         {
  20.             Title = "Rotate the Gradient Origin";
  21.             WindowStartupLocation = WindowStartupLocation.CenterScreen;
  22.             Width = 384;
  23.             Height = 384;
  24.             brush = new RadialGradientBrush(Colors.White, Colors.Blue);
  25.             brush.Center = brush.GradientOrigin = new Point(0.5, 0.5);
  26.             brush.RadiusX = brush.RadiusY = 0.10;
  27.             brush.SpreadMethod = GradientSpreadMethod.Repeat;
  28.             Background = brush;
  29.             DispatcherTimer tmr = new DispatcherTimer();    //计时器
  30.             tmr.Interval = TimeSpan.FromMilliseconds(100);  //计时器时间间隔
  31.             tmr.Tick += TimerOnTick;                        //时间间隔触发事件
  32.             tmr.Start();                                    //启动计时器
  33.         }
  34.         void TimerOnTick(object sender, EventArgs args)
  35.         {
  36.             //点pt在以客户区中心(0.5,0.5)为中心,分别以客户区宽和高的1/20
  37.             //为水平半径和垂直半径的椭圆边界上逆时针移动。
  38.             //因为这里客户区宽高比差不多是1,所以pt轨迹表现为圆。
  39.             Point pt = new Point(0.5 + 0.05 * Math.Cos(angle),
  40.                                  0.5 + 0.05 * Math.Sin(angle));
  41.             brush.GradientOrigin = pt;
  42.             angle += Math.PI / 6;      // 一次旋转30度
  43.         }
  44.     }
  45. }

程序中添加了个记时器,效果做成了动画。

在.NET中,至少有4个记时器类型,其中3个都叫做Timer。System.Threading和System.Timers内的Timer类,在我们这个例子中并不能被使用,因为这些timer事件发生在不同的线程中,而Freezable对象只能被(创建它的)同一个线程所改变,而不能被其他线程改变。System.Windows.Forms内的Timer类型封装了标准的Windows操作系统计时器,但是使用它的话,还需要在工程中加入对System.Windows.Forms.dll组件的引用。

在这里,让事件发生在WPF程序的主线程中,System.Windows.Threading命名空间的DispatcherTimer类是最适合的。

效果截图:

Windows还有个Brush属性叫BordBrush,可以在客户的周围绘制一个边框。

在上面这个程序的构造函数中添加如下代码

  1.             BorderBrush = Brushes.SaddleBrown;       //边框画笔
  2.             BorderThickness = new Thickness(25);    //边框宽度 

效果如下

Thickness( double left,  double top,  double right,  double bottom)里的几个参数分别表示边框左、上、右、下与客户区距离。它的另一个形式Thickness(double uniformLength)表示边框宽度采用统一长度,如上代码。

边框也可以使用渐变画刷,如将BorderBrush定义修改如下:

  1.             BorderBrush = new LinearGradientBrush(Colors.Red, Colors.Green,
  2.                                new Point(0, 0), new Point(1, 1));

程序运行效果如下:

由图可看出,红色在边框左上角,绿色在边框右下角,不过并不干涉客户区其它地方。其实即使BoderBrush和Background都具有相同的渐变画刷,两者也不会彼此交融

posted @ 2009-02-10 20:05  javak  阅读(329)  评论(0编辑  收藏  举报