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

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(0255255);
SolidBrushColor brush 
= new SolidBrushColor(clr);
Background 
= brush; 

用一行代码书写:

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(nullnull);
        }
    }
}

说明的是: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的两边的颜色是什么颜色,以后待说。

实例程序:

Code

效果见下图:

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

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

LinearGradientBrush brush = 
    
new LinearGradientBrush(Colors.Red, Colors.Blue, new Point(00), new Point(0.250.25));

brush.SpreadMethod 
= GradientSpreadMethod.Reflect; 

可见,(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.50.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。

原文地址:https://www.cnblogs.com/Dlonghow/p/1281172.html