基本画刷2
第01个小程序:随鼠标移动而变色的背景(VaryTheBackground.cs)
程序运行时,当鼠标指针靠近窗口中心程度不同,客户区的背景颜色也不同。
- using System;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- namespace Chapter02
- {
- public class VaryTheBackground : Window
- {
- SolidColorBrush brush = new SolidColorBrush(Colors.Black); //创建画刷 。SolidColorBrush是最简单的画刷。
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new VaryTheBackground());
- }
- public VaryTheBackground()
- {
- Title = "Vary the Background";
- Width = 384;
- Height = 384;
- Background = brush;
- }
- //鼠标移动事件
- protected override void OnMouseMove(MouseEventArgs args)
- {
- //窗口呈现尺寸-边框和标题栏的尺寸
- double width = ActualWidth
- - 2 * SystemParameters.ResizeFrameVerticalBorderWidth;
- double height = ActualHeight
- - 2 * SystemParameters.ResizeFrameHorizontalBorderHeight
- - SystemParameters.CaptionHeight;
- Point ptMouse = args.GetPosition(this); //获取鼠标位置
- Point ptCenter = new Point(width / 2, height / 2); //窗口中心
- Vector vectMouse = ptMouse - ptCenter; //向量。向:中心指向鼠标位置;量:鼠标位置与中心距离
- double angle = Math.Atan2(vectMouse.Y, vectMouse.X);
- Vector vectEllipse = new Vector(width/ 2 * Math.Cos(angle),
- height / 2 * Math.Sin(angle));
- Byte byLevel = (byte)(255 * (1 - Math.Min(1, vectMouse.Length / vectEllipse.Length)));
- Color clr = brush.Color;
- clr.R = clr.G = clr.B = byLevel;
- brush.Color = clr;
- }
- }
- }
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)
- using System;
- using System.Reflection;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- namespace Chapter02
- {
- public class FlipThroughTheBrushes : Window
- {
- int index = 0; //索引值
- PropertyInfo[] props; //属性数组
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new FlipThroughTheBrushes());
- }
- public FlipThroughTheBrushes()
- {
- //取得Brushes类的成员,放在props中
- props = typeof(Brushes).GetProperties(BindingFlags.Public | BindingFlags.Static);
- SetTitleAndBackground();
- }
- //鼠标按下时
- protected override void OnKeyDown(KeyEventArgs args)
- {
- if (args.Key == Key.Down || args.Key == Key.Up)
- {
- //Key.Down时index-1,Key.UP时index+1
- index += args.Key == Key.Up ? 1 : props.Length - 1;
- //index对属性数组长度取余,防止索引越界
- index %= props.Length;
- SetTitleAndBackground();
- }
- base.OnKeyDown(args);
- }
- //设置背景
- void SetTitleAndBackground()
- {
- Title = "Flip Through the Brushes - " + props[index].Name;
- Background = (Brush)props[index].GetValue(null, null); //设置背景画笔
- }
- }
- }
GetProperties()函数用于返回对象数组,参数是限制Brushes公开和静态的属性。其实这里可以不需要这样的限制,因为Brushes的属性本来全部都是public和static。
props[index].Name返回第index个属性的名称;props[index].GetValue(null, null)返回实际的SolidColorBrush对象,第一个参数是属性所在的对象,因为Brushes是一个静态属性,没有对应的对象,传入null;第二个参数只有在属性是索引器是才有必要。
第03个小程序:渐变画笔(GradiateTheBrush.cs)
渐变画刷的实现
- using System;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- namespace Chapter02
- {
- public class GradiateTheBrush : Window
- {
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new GradiateTheBrush());
- }
- public GradiateTheBrush()
- {
- Title = "Gradiate the Brush";
- //渐变画刷。左上角(0,0)红色,右下角(1,1)蓝色,中间渐变色。
- LinearGradientBrush brush =
- new LinearGradientBrush(Colors.Red, Colors.Blue,
- new Point(0, 0),new Point(1, 1));
- Background = brush;
- }
- }
- }
这里的表面被视为一个单位宽,一个单位高。表面的左上角是(0,0),右下角是(1,1)。
brush有个SpreadMethod属性,其默认值是GradientSpreadMethod.Pad,表示超出设定区域部分延续之前的颜色;另一个值GradientSpreadMethod.Reflect,表示按相向方向重复渐变;还有一个值GradientSpreadMethod.Repeat,表示按原始方向重复渐变。
修改LinearGradientBrush brush =……代码如下:
- LinearGradientBrush brush =
- new LinearGradientBrush(Colors.Red, Colors.Blue,
- new Point(0, 0),new Point(0.25, 0.25));
- brush.SpreadMethod = GradientSpreadMethod.Pad;
- //brush.SpreadMethod = GradientSpreadMethod.Reflect;
- //brush.SpreadMethod = GradientSpreadMethod.Repeat;
当SpreadMethod属性取不同值时,效果分别如下
第04个小程序:调整(设置)渐变参数(AdjustTheGradient.cs)
- using System;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- namespace Chapter02
- {
- class AdjustTheGradient : Window
- {
- LinearGradientBrush brush;
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new AdjustTheGradient());
- }
- public AdjustTheGradient()
- {
- Title = "Adjust the Gradient";
- //事件处理器
- SizeChanged += WindowOnSizeChanged;
- //画笔新实例。第三个参数角度为0表示从起点到终点水平渐变。
- brush = new LinearGradientBrush(Colors.Red, Colors.Blue, 0);
- //指定渐变画笔的定位坐标相对于输出区域是绝对的。
- brush.MappingMode = BrushMappingMode.Absolute;
- //背景画笔
- Background = brush;
- }
- //窗口大小改变引发事件
- void WindowOnSizeChanged(object sender, SizeChangedEventArgs args)
- {
- //获取窗口区域(没有边界,没有标题栏的区域)的宽和高
- double width = ActualWidth
- - 2 * SystemParameters.ResizeFrameVerticalBorderWidth;
- double height = ActualHeight
- - 2 * SystemParameters.ResizeFrameHorizontalBorderHeight
- - SystemParameters.CaptionHeight;
- //中心
- Point ptCenter = new Point(width / 2,height / 2);
- //窗口对角线(左下到右上)向量,即 = new Point(width, 0) new Point(0, height);
- Vector vectDiag = new Vector(width,-height);
- //垂直于对角线的向量
- Vector vectPerp = new Vector(vectDiag.Y, -vectDiag.X);
- //向量规范化,使其长为单位1。
- vectPerp.Normalize();
- //中心到起点的向量
- vectPerp *= width * height / vectDiag.Length;
- //获取渐变的二维起点和终点
- brush.StartPoint = ptCenter + vectPerp;
- brush.EndPoint = ptCenter - vectPerp;
- }
- }
- }
本程序实现的渐变方式如下图所示。假设绿色矩形区域为窗口区域,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种颜色。
- using System;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- namespace Chapter02
- {
- class FollowTheRainbow : Window
- {
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new FollowTheRainbow());
- }
- public FollowTheRainbow()
- {
- Title = "Follow the Rainbow";
- LinearGradientBrush brush = new LinearGradientBrush();
- brush.StartPoint = new Point(0, 0);
- brush.EndPoint = new Point(1, 0);
- Background = brush;
- // 设置画笔渐变停止点
- brush.GradientStops.Add(new GradientStop(Colors.Red, 0));
- brush.GradientStops.Add(new GradientStop(Colors.Orange, .17));
- brush.GradientStops.Add(new GradientStop(Colors.Yellow, .33));
- brush.GradientStops.Add(new GradientStop(Colors.Green, .5));
- brush.GradientStops.Add(new GradientStop(Colors.Blue, .67));
- brush.GradientStops.Add(new GradientStop(Colors.Indigo, .84));
- brush.GradientStops.Add(new GradientStop(Colors.Violet, 1));
- }
- }
- }
GradientStops属性是GradientStopCollection类型,这是GradientStop对象的集合。
GradientStop(Color color, Double offset),offset值范围在0和1之间,是StartPoint和EndPoint的相对距离。
效果图:
第06个小程序:圆形彩虹(CircleTheRainbow.cs)
由客户中心向外画彩虹的7种颜色。
- using System;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- namespace Chapter02
- {
- public class CircleTheRainbow : Window
- {
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new CircleTheRainbow());
- }
- public CircleTheRainbow()
- {
- Title = "Circle the Rainbow";
- // 定义径向渐变画刷
- RadialGradientBrush brush = new RadialGradientBrush();
- Background = brush;
- // 设置画笔渐变停止点
- brush.GradientStops.Add(new GradientStop(Colors.Red, 0));
- brush.GradientStops.Add(new GradientStop(Colors.Orange, .17));
- brush.GradientStops.Add(new GradientStop(Colors.Yellow, .33));
- brush.GradientStops.Add(new GradientStop(Colors.Green, .5));
- brush.GradientStops.Add(new GradientStop(Colors.Blue, .67));
- brush.GradientStops.Add(new GradientStop(Colors.Indigo, .84));
- brush.GradientStops.Add(new GradientStop(Colors.Violet, 1));
- }
- }
- }
RadialGradientBrush的许多属性都已经具备实用的默认值。基中三个属性用来定义一个椭圆:Center是Point类型,定义为(0.5,0.5),即画刷涵盖区域中心点(还有个GradientOrigin,和Center一样)。RadiusX和RadiusY的属性为double类型,分别代表椭圆的水平和垂直轴半径,默认值是0.5,所以无论是横向还是纵向,椭圆都达到了当前画刷作用区域的边界。
在椭圆以外的区域(屏幕窗口的四个角落),将继续使用紫色,因为SpreadMethod的默认值是Fill。
本程序效果:
将以上代码作下修改(CircleTheRainbow.cs):
- using System;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Reflection;
- namespace Chapter02
- {
- public class CircleTheRainbow : Window
- {
- double c = 0;
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new CircleTheRainbow());
- }
- public CircleTheRainbow()
- {
- Title = "Circle the Rainbow";
- Width = 640;
- Height = 320;
- Draw();
- }
- private void Draw()
- {
- double b = 1.0 / 10;
- // 定义径向渐变画刷
- RadialGradientBrush brush = new RadialGradientBrush();
- brush.RadiusX = 0.8;
- brush.RadiusY = 0.7;
- Background = brush;
- // 设置画笔渐变停止点
- brush.GradientStops.Add(new GradientStop(Colors.Red, (0 + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.Orange, (b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.Yellow, (2 * b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.Green, (3 * b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.Blue, (4 * b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.Indigo, (5 * b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.Violet, (6 * b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.YellowGreen, (7 * b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.WhiteSmoke, (8 * b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.Transparent, (9 * b + c) % 1));
- brush.GradientStops.Add(new GradientStop(Colors.RosyBrown, (10 * b + c) % 1));
- }
- //按键
- protected override void OnKeyDown(KeyEventArgs args)
- {
- base.OnKeyDown(args);
- //按下空格
- if (args.Key == Key.Space)
- {
- c += 0.005;
- Draw();
- }
- }
- }
- }
运行,这回按下空格键时,会看到各椭圆色圈从中心向外扩展,像石子引起的水波一样。
第07个小程序:点击渐变中心(ClickTheRadientCenter .cs)
- using System;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- namespace Chapter02
- {
- class ClickTheRadientCenter : Window
- {
- RadialGradientBrush brush;
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new ClickTheRadientCenter());
- }
- public ClickTheRadientCenter()
- {
- Title = "Click the Gradient Center";
- brush = new RadialGradientBrush(Colors.White, Colors.Red);
- //椭圆的水平和垂直半径分别为窗口宽和高的十分之一
- brush.RadiusX = brush.RadiusY = 0.10;
- //渐变绘制方式
- brush.SpreadMethod = GradientSpreadMethod.Repeat;
- Background = brush;
- }
- // 鼠标按下
- protected override void OnMouseDown(MouseButtonEventArgs args)
- {
- //获取窗口区域(没有边界,没有标题栏的区域)的宽和高
- double width = ActualWidth
- - 2 * SystemParameters.ResizeFrameVerticalBorderWidth;
- double height = ActualHeight
- - 2 * SystemParameters.ResizeFrameHorizontalBorderHeight
- - SystemParameters.CaptionHeight;
- Point ptMouse = args.GetPosition(this);
- //因为是采用的是相对坐标系统,鼠标指针位置也要换算成相对(宽和高)的。
- ptMouse.X /= width;
- ptMouse.Y /= height;
- // 鼠标左击
- if (args.ChangedButton == MouseButton.Left)
- {
- brush.Center = ptMouse;
- brush.GradientOrigin = ptMouse;
- }
- //鼠标右击
- else if (args.ChangedButton == MouseButton.Right)
- brush.GradientOrigin = ptMouse;
- }
- }
- }
GradientOrigin 是渐变开始的原点,在GradientOrigin 和椭圆圆周之间,就是发生渐变的地方。如果GradientOrigin 等于Center(默认如此),那么渐变会从椭圆圆心到椭圆圆周之间扩散。如果GradientOrigin 和Center之间有一段距离,那么在GradientOrigin 和最接近的椭圆圆周之间,渐变的颜色变化会比较剧烈,相反方向的颜色变化就比较缓和。
本程序就是演示这两种效果。鼠标左击时,GradientOrigin 等于Center,即使它们的值(位置)变了,结果依然是从椭圆中心到边界的渐变;但当鼠标右击时,GradientOrigin 不等于Center,只有GradientOrigin改变,则出现挤压或拉伸的状态。
效果如下:
第08个小程序:旋转渐变原点(RotateTheGradientOrigin .cs)
- using System;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Threading;
- namespace Chapter02
- {
- public class RotateTheGradientOrigin : Window
- {
- RadialGradientBrush brush;
- double angle;
- [STAThread]
- public static void Main()
- {
- Application app = new Application();
- app.Run(new RotateTheGradientOrigin());
- }
- public RotateTheGradientOrigin()
- {
- Title = "Rotate the Gradient Origin";
- WindowStartupLocation = WindowStartupLocation.CenterScreen;
- Width = 384;
- 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 += TimerOnTick; //时间间隔触发事件
- tmr.Start(); //启动计时器
- }
- void TimerOnTick(object sender, EventArgs args)
- {
- //点pt在以客户区中心(0.5,0.5)为中心,分别以客户区宽和高的1/20
- //为水平半径和垂直半径的椭圆边界上逆时针移动。
- //因为这里客户区宽高比差不多是1,所以pt轨迹表现为圆。
- Point pt = new Point(0.5 + 0.05 * Math.Cos(angle),
- 0.5 + 0.05 * Math.Sin(angle));
- brush.GradientOrigin = pt;
- angle += Math.PI / 6; // 一次旋转30度
- }
- }
- }
程序中添加了个记时器,效果做成了动画。
在.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,可以在客户的周围绘制一个边框。
在上面这个程序的构造函数中添加如下代码
- BorderBrush = Brushes.SaddleBrown; //边框画笔
- BorderThickness = new Thickness(25); //边框宽度
效果如下
Thickness( double left, double top, double right, double bottom)里的几个参数分别表示边框左、上、右、下与客户区距离。它的另一个形式Thickness(
边框也可以使用渐变画刷,如将BorderBrush定义修改如下:
- BorderBrush = new LinearGradientBrush(Colors.Red, Colors.Green,
- new Point(0, 0), new Point(1, 1));
程序运行效果如下:
由图可看出,红色在边框左上角,绿色在边框右下角,不过并不干涉客户区其它地方。其实即使BoderBrush和Background都具有相同的渐变画刷,两者也不会彼此交融
正面猛男