今天同学给我一个可以在桌面下雪的动态桌面软件. 很有意思.与普通的动态桌面不同的,它并非在桌面设置背景Web页. 而是直接画在整个屏幕上的. 如图.如果仿照其设计思路,那就可以做一个其它类型的动态桌面程序.
那就做一个黑客帝国中流动文字程序.大家先看一下效果,觉得值得看,再往下看.下载完整源码
要做的第一步是,得到一个包含各国文字的随机字符串. 这个好办,可以根据各国文字的代码页,得到编码器,并用其编码得到随机字符串.
/// Name:ks_c_5601-1987 DisplayName:Korean CodePage:949
/// </summary>
private Encoding korean = Encoding.GetEncoding( 949 );
/**/ /// <summary>
/// Name:gb2312 DisplayName:Chinese Simplified (GB2312) CodePage:936
/// </summary>
private Encoding chs = Encoding.GetEncoding( 936 );
/**/ /// <summary>
/// Name:big5 DisplayName:Chinese Traditional (Big5) CodePage:950
/// </summary>
private Encoding cht = Encoding.GetEncoding( 950 );
第二步,准备用到的本地函数:因为一个字符串显示完了,后有逐字删除的效果,所以对,绘过区域要进行重绘.还要获是屏幕窗口句柄, 如果只在桌面绘制,需要得到包含图标窗口的句柄.
/// 获得桌面含图标窗口的句柄,不是全屏窗口句柄
/// </summary>
/// <returns></returns>
public static IntPtr GetDesktopIconWnd()
... {
IntPtr hResult;
hResult = FindWindow("ProgMan", null);
hResult = GetWindow(hResult, 5);
hResult = GetWindow(hResult, 5);
return hResult;
}
第三步,有准备多种色彩的随机刷子数组.
第四步,绘制字符串.字符串对像(Texter)它包含了一个郁机位置,字符就从这里开始以竖直方式绘制,为了实现字符串向下流动的效果,设计一个绘制字符串的机制. 有这了个字符串对象集合(Drawer),它有一个绘制字符串的时钟周期.在一个时钟周期内,它迭代它所包有字符串对象,并调用字符串对象的void Draw(Graphics g)方法. 在字符串对象绘制自已的字符串时,一个周期内只绘制两个字符,第一个为随机色,第二个为白色. 当在下一个周期绘制时,会把上一次的白色字符重新绘制成随机色,把第下一个字符绘制成白色.如此下去,就有一个白色字符下坠的效果.
/// 字符串增长绘制
/// </summary>
/// <param name="g"></param>
private void Increase(Graphics g)
... {
float charY = location.Y + drawedChar * CHARHEIGHT;
if (drawedChar >= text.Length || charY >= height)
...{
increaseDrawText = false;
return;
}
string chart = string.Format("{0}", text[drawedChar]);
g.DrawString(chart, font, RndBrush.DrawBursh, new PointF(location.X, charY));
drawedChar++;
charY = location.Y + drawedChar * CHARHEIGHT;
if (drawedChar < text.Length - 1)//绘制白色字
...{
chart = string.Format("{0}", text[drawedChar]);
g.DrawString(chart, font, RndBrush.White, new PointF(location.X, charY));
}
}
当字符串对象把自已的字符串绘制完后,开始从上到下,一个周期内删除一个字符,直到"删除"完毕.删除操作,并不是真正的删除,而是指定字符所在的区域为无效,并让窗体重绘该区域. 也许你认为让窗口每次都重绘不就得了,可以是可以,但是你会看到屏幕一直在闪,这就不爽了.所以此举有副作用,弃之.
当字符删除完后,它的一个生命周期结束,重新生成新的随机字符串,和随机位置.然后重复上面的操作.
/// 字符串减少绘制
/// </summary>
/// <param name="g"></param>
private void Reduce(Graphics g)
... {
int charY = location.Y + redeceChar * CHARHEIGHT;
redeceChar++;
if (redeceChar >= drawedChar || charY >= height)
...{
this.text = Texter.rndText.GetRandomText(rnd.Next(minLeng, maxLeng));
this.location = RndLocation;
this.increaseDrawText = true;
this.drawedChar = this.redeceChar = 0;
return;
}
invalidateEventArgs.Region = new Rectangle(location, new Size(20, charY));
OnRegionInvalidate(invalidateEventArgs);
}
/**/ /// <summary>
/// 字符所在的区域无效
/// </summary>
/// <param name="e"></param>
protected void OnRegionInvalidate(InvalidateEventArgs e)
... {
if (RegionInvalidate != null)
...{
RegionInvalidate(this, e);
}
}
/// 重绘字符区域
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void t_RegionInvalidate( object sender, InvalidateEventArgs e)
... {
Rect rect = Rect.FromXYWH(e.Region.X, e.Region.Y, e.Region.Width, e.Region.Height);
NativeMethod.RedrawWindow(hWnd, ref rect, IntPtr.Zero,
RedrawFlag.RDW_ERASE
| RedrawFlag.RDW_INVALIDATE
| RedrawFlag.RDW_ERASENOW
| RedrawFlag.RDW_ALLCHILDREN);
}
第五步,绘制器(字符串对象集合Drawer),一个时钟周期内迭代字符串对像,并绘制.
构造函数.
... {
maxLeng = 15;
minLeng = 5;
velocity = 25;
this.textCount = 50;
this.hWnd = hWnd;
Rect rect = new Rect();
NativeMethod.GetWindowRect(hWnd, ref rect);
areaSize = rect.Size;
this.graphics = Graphics.FromHdc(NativeMethod.GetWindowDC(hWnd));
texters = new List<Texter>(128);
timerDraw = new Timer();
timerDraw.Interval = velocity;
timerDraw.Tick += new EventHandler(timerDraw_Tick);
timerBalanceTexter = new Timer();
timerBalanceTexter.Interval = 1000;
timerBalanceTexter.Tick += new EventHandler(timerBalanceTexter_Tick);
}
... {
this.Draw(this.graphics);
}
private void Draw(Graphics g)
... {
foreach (Texter t in this.texters)
...{
t.Draw(g);
}
}
这样就做完了.