在做一个WinForm登录框时,突然想到,如果有黑客帝国中字符雨的特效做背景,那应该蛮Cool的,所以就有了如下代码,随意写的,有点乱。
public partial class CharacterRain : Component

{

/**//// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;


/**//// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)

{
if (RainThread != null && RainThread.IsAlive)
Stop();

if (disposing && (components != null))

{
components.Dispose();
}
base.Dispose(disposing);
}


组件设计器生成的代码#region 组件设计器生成的代码


/**//// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()

{
components = new System.ComponentModel.Container();
}

#endregion

public CharacterRain()

{
Initialize();

InitializeComponent();
}

public CharacterRain(IContainer container)

{
Initialize();

container.Add(this);

InitializeComponent();
}

private object BMPLock = new object();
private Bitmap BMP;
private Graphics Graph;

private void Initialize()

{
this.DrawRainEvent = new EventHandler(CharacterRainPanel_DrawRainEvent);
OnPaint = new PaintEventHandler(ShowWindow_Paint);
}

private PaintEventHandler OnPaint;


private char[] CHARACTERS =
{ \'`\', \'1\', \'2\', \'3\', \'4\', \'5\', \'6\', \'7\', \'8\', \'9\', \'0\', \'-\', \'=\', \'\\\', \'q\', \'w\', \'e\', \'r\', \'t\', \'y\', \'u\', \'i\', \'o\', \'p\', \'[\', \']\', \'a\', \'s\', \'d\', \'f\', \'g\', \'h\', \'j\', \'k\', \'l\', \';\', \'\\'\', \'z\', \'x\', \'c\', \'v\', \'b\', \'n\', \'m\', \',\', \'.\', \'/\', \'~\', \'!\', \'@\', \'#\', \'$\', \'%\', \'^\', \'&\', \'*\', \'(\', \')\', \'_\', \'+\', \'|\', \'Q\', \'W\', \'E\', \'R\', \'T\', \'Y\', \'U\', \'I\', \'O\', \'P\', \'
{\', \'}\', \'A\', \'S\', \'D\', \'F\', \'G\', \'H\', \'J\', \'K\', \'L\', \':\', \'"\', \'Z\', \'X\', \'C\', \'V\', \'B\', \'N\', \'M\', \'<\', \'>\', \'?\' };
private static readonly Random Rand = new Random();

private Control _ShowWindow = null;

/**//// <summary>
/// 获取、设置用于显示字符雨的窗口(或控件)
/// </summary>
public Control ShowWindow

{

get
{ return _ShowWindow; }
set

{
if (_ShowWindow != null)
_ShowWindow.Paint -= OnPaint;

_ShowWindow = value;

if (_ShowWindow != null)
_ShowWindow.Paint += OnPaint;
}
}

void ShowWindow_Paint(object sender, PaintEventArgs e)

{
//Paint();
if (BMP != null)

{
lock (BMPLock)

{
//e.Graphics.Clear(_ShowWindow.BackColor);
try

{
e.Graphics.DrawImage(BMP, 0, 0);
}
catch

{
}
}
}
}

private string _RainCharacters = "`1234567890-=\\qwertyuiop[]asdfghjkl;\'zxcvbnm,./~!@#$%^&*()_+|QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?";

/**//// <summary>
/// 获取、设置字符雨中可能出现的字符
/// </summary>
public string RainCharacters

{

get
{ return _RainCharacters; }
set

{
if (string.IsNullOrEmpty(value))
value = "`1234567890-=\\qwertyuiop[]asdfghjkl;\'zxcvbnm,./~!@#$%^&*()_+|QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?";
_RainCharacters = value;
CHARACTERS = value.ToCharArray();
}
}

private Color _RainHeadColor = Color.Lime;

/**//// <summary>
/// 获取、设置字符雨头的颜色
/// </summary>
public Color RainHeadColor

{

get
{ return _RainHeadColor; }
set

{
if (!Running)
_RainHeadColor = value;
else
throw new Exception("运行中不可以更改颜色。");
}
}


private Color _RainBodyColor = Color.Green;

/**//// <summary>
/// 获取、设置字符雨体的颜色
/// </summary>
public Color RainBodyColor

{

get
{ return _RainBodyColor; }
set

{
if (!Running)
_RainBodyColor = value;
else
throw new Exception("运行中不可以更改颜色。");
}
}

private Color _RainTailColor = Color.DarkGreen;

/**//// <summary>
/// 获取、设置字符雨体的颜色
/// </summary>
public Color RainTailColor

{

get
{ return _RainTailColor; }
set

{
if (!Running)
_RainTailColor = value;
else
throw new Exception("运行中不可以更改颜色。");
}
}


private Font _RainFont = new Font(new FontFamily("Consolas"), 12);

/**//// <summary>
/// 获取、设置字符雨的字体样式
/// </summary>
public Font RainFont

{

get
{ return _RainFont; }
set

{
if (!Running)
_RainFont = value;
else
throw new Exception("运行中不可以更改字体。");
}
}

private int _StreamsCount = 100;

/**//// <summary>
/// 获取、设置字符雨的数量
/// </summary>
public int StreamsCount

{

get
{ return _StreamsCount; }
set

{
if (!Running)
_StreamsCount = value;
else
throw new Exception("运行中不可以更改数量。");
}
}

private int _MaxLength = 50;

/**//// <summary>
/// 获取、设置每个雨滴的最大字符数
/// </summary>
public int MaxLength

{

get
{ return _MaxLength; }
set

{
if (!Running)

{
if (value >= _MinLength)
_MaxLength = value;
else
throw new Exception("最大字符数不能小于最小字符数。");
}
else
throw new Exception("运行中不可以更改数量。");
}
}

private int _MinLength = 20;

/**//// <summary>
/// 获取、设置每个雨滴的最小字符数
/// </summary>
public int MinLength

{

get
{ return _MinLength; }
set

{
if (!Running)

{
if (value <= _MaxLength)
_MinLength = value;
else
throw new Exception("最小字符数不能大于最大字符数。");
}
else
throw new Exception("运行中不可以更改数量。");
}
}

public bool Running

{

get
{ return RainThread != null && RainThread.IsAlive; }
}

private Thread RainThread;
private bool RUN;
private bool PAUSE;
private Brush BackBrush = null;

private Brush RainHeadBrush = null;
private Brush RainBodyBrush = null;
private Brush RainTailBrush = null;


/**//// <summary>
/// 开始显示字符雨
/// </summary>
public void Start()

{
if (_ShowWindow == null)
throw new Exception("没有用于显示的窗口(或控件)");

if (!Running)

{
RainThread = new Thread(new ParameterizedThreadStart(RainProcess));
RUN = true;
PAUSE = false;
Ready();
RainThread.Start();
}
}

private void Ready()

{
Rain.RainFont = _RainFont;
Rain.RainHeadColor = _RainHeadColor;
Rain.RainBodyColor = _RainBodyColor;
Rain.RainTailColor = _RainTailColor;
Rain.BackColor = _ShowWindow.BackColor;

if (RainHeadBrush != null)
RainHeadBrush.Dispose();
if (RainBodyBrush != null)
RainBodyBrush.Dispose();
if (RainTailBrush != null)
RainTailBrush.Dispose();
if (BackBrush != null)
BackBrush.Dispose();

RainHeadBrush = new SolidBrush(Rain.RainHeadColor);
RainBodyBrush = new SolidBrush(Rain.RainBodyColor);
RainTailBrush = new SolidBrush(Rain.RainTailColor);
BackBrush = new SolidBrush(Rain.BackColor);

//RainItems.Clear();

using (Graphics g = _ShowWindow.CreateGraphics())

{
g.Clear(Rain.BackColor);
}
}

private EventHandler DrawRainEvent;

void CharacterRainPanel_DrawRainEvent(object sender, EventArgs e)

{
//foreach (Rain.Item item in e.Items)
//{
// _ShowWindow.Invalidate(new Rectangle(item.X, item.Y, item.Width, item.Height));
// _ShowWindow.Update();
//}

using (Graphics g = _ShowWindow.CreateGraphics())

{
g.DrawImage(BMP, 0, 0);
}
//_ShowWindow.Invalidate(true);
//_ShowWindow.Refresh();
}

//private List<Rain.Item> RainItems = new List<Rain.Item>();
private object RainLOCK = new object();

private void RainProcess(object Param)

{
using (BMP = new Bitmap(_ShowWindow.Width, _ShowWindow.Height))

{
using (Graph = Graphics.FromImage(BMP))

{

List<Rain> Rains = new List<Rain>();
for (int i = 0; i < _StreamsCount; i++)
Rains.Add(new Rain(_MinLength + Rand.Next(_MaxLength - _MinLength)));

while (RUN)

{
if (PAUSE)

{
Thread.Sleep(300);
continue;
}

DoRain(Rains);

Thread.Sleep(50);
}

foreach (Rain r in Rains)

{
r.Uninit();
}
}
}
}

private void DoRain(List<Rain> Rains)

{

for (int i = 0; i < Rains.Count; i++)

{
Rain rain = Rains[i];

if (!rain.Inited)

{
if (!rain.Init(_ShowWindow.Width, Rand.Next(_ShowWindow.Height / 3)))
continue;
}

Rain.Item item = rain.GetItem(CHARACTERS[Rand.Next(CHARACTERS.Length)]);
Rain.Item prior = null;

if (item == null)
continue;
else

{
lock (BMPLock)

{
if (item.Y <= BMP.Height)

{
if (item.Type == Rain.Item.RainItemType.Head && rain.Prior.Type != Rain.Item.RainItemType.Tail)

{
rain.Prior.Type = Rain.Item.RainItemType.Body;
prior = rain.Prior;
}

if (item.Type == Rain.Item.RainItemType.Back)

{
prior = rain.Prior;
}

switch (item.Type)

{
case Rain.Item.RainItemType.Head:

{
Graph.DrawString(new string(item.CH, 1), _RainFont, RainHeadBrush, item.X, item.Y);
break;
}
case Rain.Item.RainItemType.Body:

{
Graph.DrawString(new string(item.CH, 1), _RainFont, RainBodyBrush, item.X, item.Y);
break;
}
case Rain.Item.RainItemType.Tail:

{
Graph.DrawString(new string(item.CH, 1), _RainFont, RainTailBrush, item.X, item.Y);
break;
}
case Rain.Item.RainItemType.Back:

{
Graph.FillRectangle(BackBrush, item.X, item.Y, item.Width, item.Height);
break;
}
}

if (prior != null)

{
switch (prior.Type)

{
case Rain.Item.RainItemType.Head:

{
Graph.DrawString(new string(prior.CH, 1), _RainFont, RainHeadBrush, prior.X, prior.Y);
break;
}
case Rain.Item.RainItemType.Body:

{
Graph.DrawString(new string(prior.CH, 1), _RainFont, RainBodyBrush, prior.X, prior.Y);
break;
}
case Rain.Item.RainItemType.Tail:

{
Graph.DrawString(new string(prior.CH, 1), _RainFont, RainTailBrush, prior.X, prior.Y);
break;
}
case Rain.Item.RainItemType.Back:

{
Graph.FillRectangle(BackBrush, prior.X, prior.Y, prior.Width, prior.Height);
break;
}
}
}
}
else

{
item.Owner.Way = Rain.RainWay.Reverse;
}
}
}
}

try

{
if (!_ShowWindow.Disposing)
_ShowWindow.Invoke(DrawRainEvent, this, new EventArgs());
}
catch

{
}
}


/**//// <summary>
/// 停止显示字符雨
/// </summary>
public void Stop()

{
RUN = false;
}


/**//// <summary>
/// 暂停显示
/// </summary>
public void Pause()

{
PAUSE = true;
}


/**//// <summary>
/// 恢复暂停
/// </summary>
public void Resume()

{
PAUSE = false;
}

//protected void Paint()
//{
// lock (RainLOCK)
// {
// for (int i = RainItems.Count - 1; i >= 0; i--)
// {
// Rain.Item item = RainItems[i];

// if (item.X < BMP.Width)
// {
// if (item.Y < BMP.Height)
// {
// //if (e.ClipRectangle.X <= item.X && e.ClipRectangle.Y <= item.Y && e.ClipRectangle.Width >= item.Width && e.ClipRectangle.Height >= item.Height)
// {
// switch (item.Type)
// {
// case Rain.Item.RainItemType.Head:
// {
// e.Graphics.DrawString(new string(item.CH, 1), _RainFont, RainHeadBrush, item.X, item.Y);
// break;
// }
// case Rain.Item.RainItemType.Body:
// {
// e.Graphics.DrawString(new string(item.CH, 1), _RainFont, RainBodyBrush, item.X, item.Y);
// break;
// }
// case Rain.Item.RainItemType.Tail:
// {
// e.Graphics.DrawString(new string(item.CH, 1), _RainFont, RainTailBrush, item.X, item.Y);
// break;
// }
// case Rain.Item.RainItemType.Back:
// {
// e.Graphics.FillRectangle(BackBrush, item.X, item.Y, item.Width, item.Height);
// break;
// }
// }
// }
// }
// else
// {
// item.Owner.Way = Rain.RainWay.Reverse;
// }
// }

// if (item.Type == Rain.Item.RainItemType.Back)
// {
// RainItems.Remove(item);
// }
// }
// }
//}

private class Rain

{
private static Font _RainFont;
public static Font RainFont

{

get
{ return _RainFont; }
set

{
_RainFont = value;

_RainHeight = (int)(value.Size * value.FontFamily.GetLineSpacing(FontStyle.Regular) / value.FontFamily.GetEmHeight(FontStyle.Regular)) + 1;
}
}

public static Color BackColor;

public static Color RainBodyColor;

public static Color RainHeadColor;

public static Color RainTailColor;

private static int _RainHeight;
public static int RainHeight

{

get
{ return _RainHeight; }
}

private static object RangeLock = new object();
private static List<Rectangle> Ranges = new List<Rectangle>();

Queue<Item> Chars = new Queue<Item>();
private int _Length;
private int _BeginX;
private int _BeginY;

private RainWay _Way;

public RainWay Way

{

get
{ return _Way; }

set
{ _Way = value; }
}

private Rectangle Range;

public enum RainWay : byte

{

/**//// <summary>
/// 正向
/// </summary>
Obverse = 0,


/**//// <summary>
/// 反向
/// </summary>
Reverse = 1
}

public bool Inited

{

get
{ return Chars.Count > 0; }
}

public bool Init(int Width, int Y)

{
bool Ret = true;

Uninit();

if (Rand.Next(30) < 10)
return false;

_BeginX = Rand.Next(Width);

Size s = TextRenderer.MeasureText(" ", _RainFont);

Range = new Rectangle(_BeginX, Y, s.Width, s.Height * _Length);

foreach (Rectangle r in Ranges)

{
if (r.Contains(this.Range) || r.IntersectsWith(this.Range))

{
Ret = false;
break;
}
}

if (Ret)
Ranges.Add(this.Range);

_BeginY = Y;
_Way = RainWay.Obverse;
_Prior = null;

return Ret;
}

public void Uninit()

{
if (this.Range != null && Ranges.Contains(this.Range))
Ranges.Remove(this.Range);
}


item#region item
public class Item

{
private Rain _Owner;
public Rain Owner

{

get
{ return _Owner; }
}

public enum RainItemType : byte

{
Head = 0,
Body = 1,
Tail = 2,
Back = 3
}

private char _CH;
public char CH

{

get
{ return _CH; }
}

private int _X;
public int X

{

get
{ return _X; }
}

private int _Y;
public int Y

{

get
{ return _Y; }
}

private int _Width;
public int Width

{

get
{ return _Width; }
}

private int _Height;
public int Height

{

get
{ return _Height; }
}

private RainItemType _Type;
public RainItemType Type

{

get
{ return _Type; }

set
{ _Type = value; }
}

public Item(Rain Owner, char CH, int X, int Y, RainItemType Type)

{
this._Owner = Owner;
this._CH = CH;
this._X = X;
this._Y = Y;
Size s = TextRenderer.MeasureText(new string(CH, 1), RainFont);
this._Width = s.Width;
this._Height = s.Height;
this._Type = Type;
}
}
#endregion

public Rain(int Length)

{
_Length = Length;
}

public Item GetItem(char CH)

{
Item Ret = null;
if (_Way == RainWay.Obverse)

{
Ret = Add(CH);
if (Ret == null)
_Way = RainWay.Reverse;
}
else

{
Ret = Remove();
if (Ret == null)
_Way = RainWay.Obverse;
}
return Ret;
}

private Item _Prior = null;
public Item Prior

{
get

{
return _Prior;
}
}

private Item Add(char CH)

{
Item Ret = null;
if (Chars.Count < _Length)

{
Ret = new Item(this, CH, _BeginX, _BeginY + RainHeight * Chars.Count, Chars.Count == 0 ? Item.RainItemType.Tail : Item.RainItemType.Head);
if (Ret.Type == Item.RainItemType.Head)
_Prior = Chars.Last();
Chars.Enqueue(Ret);
}

return Ret;
}

private Item Remove()

{
Item Ret = null;
if (Chars.Count > 0)

{
Ret = Chars.Dequeue();
Ret.Type = Item.RainItemType.Back;

if (Chars.Count > 0)

{
_Prior = Chars.First();
_Prior.Type = Item.RainItemType.Tail;
}
else
_Prior = null;
}
return Ret;
}
}
}

测试程序下载
使用的时候,先设定ShowWindow,这个属性决定在哪个控件上显示字符雨,然后还可以设置如下属性:
MaxLength:每条字符雨的最大字符数量
MinLength:每条字符雨的最小字符数量
RainBodyColor:字符雨中间的颜色
RainCharacters:字符雨可以使用到的字符
FainFont:字体
RainHeadColor:最前面的字符的颜色
RainTailColor:最后面字符的颜色
StreamsCount:字符雨的数量
还有如下方法可供调用:
Start:开始显示
Stop:停止显示
Pause:暂停显示
Resume:恢复暂停