如何在 Windows 桌面上创建暴风雪?
Posted
技术标签:
【中文标题】如何在 Windows 桌面上创建暴风雪?【英文标题】:How to create a snowstorm on your Windows desktop? 【发布时间】:2011-06-07 04:39:35 【问题描述】:抛开实际用途不谈,如何(如果可能的话)在运行 Windows 的台式 PC 上创建“下雪”效果?最好只使用原始 C/C++ 和 WinAPI。
对雪的要求是:
出现在显示的所有其他内容上(注意:始终在顶部的窗口可能仍然会覆盖在雪上,这没关系。我知道任何应用程序都不能有“绝对在顶部”标志) 雪花很小,可能是几个白色像素的简单点或簇; 不必费心使用计算机(点击雪花会将点击发送到底层窗口); 非常适合用户拖动窗口; 支持多显示器。以下任何功能的奖励积分:
窗口或任务栏(如果位于屏幕底部)的下边缘会积雪; 积雪也会积聚在顶层窗口上。或者也许一些积雪,一些继续向下,积聚在每个带有标题栏的窗口上; 拖动窗口时,窗口上积雪会“抖掉”; 任务栏上的积雪知道 Vista/7 下扩展的“开始”按钮。 雪花有阴影/轮廓,因此它们在白色背景上可见; 雪花具有类似雪花的复杂形状(它们必须仍然很小)。 点击雪花确实会将点击发送到底层窗口,但雪花会随着一点很酷的动画消失;这些效果中的大部分都很简单,除了雪是点击的部分,并且可以很好地与窗口的拖动配合使用。在我早期的时候,我做了一个利用你从 GetDesktopWindow()
获得的 HDC 的实现,它是点击通过的,但是在用户拖动窗口时遇到了问题(在它们上呈现的雪花被“拖着”)。
该解决方案可能使用 Vista/7 Aero 功能,但当然,首选通用解决方案。有什么想法吗?
【问题讨论】:
今晚我可以把我的电脑放在纽约我家门前的台阶上,满足所有 5 项要求。 @Larry Lustig - 点了,但是我希望有一个更多的软件解决方案,将它发布在 SO 和所有... 致反对者(密切的选民)——你不明白什么?我要澄清更多吗? 在你“早期”做这件事的时候,layered windows 在附近吗? extended style flagsWS_EX_LAYERED
和 WS_EX_TRANSPARENT
的窗口可能正是您想要的。
@Cody Gray - 不,他们不是。那是 10 年前的事了,我才刚刚开始我的代码之路。 :)
【参考方案1】:
为简洁起见,此答案已被精简为一组有限的要求。扩展它并使其更健壮是微不足道的。
此答案在 Windows XP 上使用 WPF。它最多可以在 2 个显示器上运行,并且也应该可以在其他 Windows 系统上运行。
从一个简单的窗口开始:
<Window x:Class="TestDump.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" WindowStartupLocation="Manual" Loaded="Window_Loaded"
WindowStyle="None" AllowsTransparency="True" Background="Transparent"
>
<Grid x:Name="FieldOfSnow"/>
</Window>
在这个窗口中,我们将添加如下定义的雪花:
<UserControl x:Class="TestDump.SnowFlake"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="5" Width="5">
<Border Background="White" CornerRadius="2" BorderThickness="1" BorderBrush="LightGray"/>
</UserControl>
雪花具有默认的 UserControl 代码隐藏,没有任何更改。
最后,Window CodeBehind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Windows.Interop;
namespace TestDump
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
private TimeSpan _lastRender;
public Window1()
InitializeComponent();
_lastRender = TimeSpan.FromTicks(0);
CompositionTarget.Rendering += SnowflakeTick;
private void Window_Loaded(object sender, RoutedEventArgs e)
this.Topmost = true;
this.Top = 0;
this.Left = 0;
this.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
if (System.Windows.Forms.SystemInformation.MonitorCount == 2)
System.Drawing.Rectangle SecondScreenArea = System.Windows.Forms.Screen.AllScreens[1].Bounds;
this.Width += SecondScreenArea.Width;
this.Height = this.Height > SecondScreenArea.Height ? this.Height : SecondScreenArea.Height;
public const int WS_EX_TRANSPARENT = 0x00000020;
public const int GWL_EXSTYLE = (-20);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
protected override void OnSourceInitialized(EventArgs e)
base.OnSourceInitialized(e);
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(this).Handle;
// Change the extended window style to include WS_EX_TRANSPARENT
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
List<TranslateTransform> Flakes = new List<TranslateTransform>();
Random rand = new Random();
private void SnowflakeTick(object sender, EventArgs e)
RenderingEventArgs renderingArgs = (RenderingEventArgs)e;
TimeSpan dTime = (renderingArgs.RenderingTime - _lastRender);
double deltaTime = dTime.TotalMilliseconds;
_lastRender = renderingArgs.RenderingTime;
if ( _lastRender.Milliseconds < deltaTime)
TranslateTransform SnowPos = new TranslateTransform(this.Width * rand.Next(1000) / 1000.0 - this.Width/2, -this.Height/2);
SnowFlake sf = new SnowFlake();
sf.RenderTransform = SnowPos;
// The flakes are centered when added, so all positions must be translated with this in mind.
FieldOfSnow.Children.Add(sf);
Flakes.Add(SnowPos);
foreach (TranslateTransform Flake in Flakes)
double ScreenHeight = this.Height / 2 - 2;
if (Flake.Y < ScreenHeight)
Flake.Y += deltaTime / 50;
我必须使用一些表单代码来获取多屏内容,并且我必须在我的项目中包含对程序集的引用。
我没有对它进行太多测试,但它确实在我的系统上运行,完成后雪会位于屏幕底部。
我使用this 引用作为点击行为。
一个比我更敬业的人应该能够适应this,加上一些边缘检测来完成让雪落到别处的任务。
请注意,此示例中从未清理过雪花,并且在运行足够长的时间后,您可能会注意到速度有所下降。
玩得开心!
【讨论】:
我看到你的技巧也是 WS_EX_TRANSPARENT(又名分层窗口)。感谢您确认这有效! :) 知道如何用 Java 制作它吗? @Alpine - 这是一个比在评论中可以回答的更棘手的问题。你为什么不发布一个单独的问题?以上是关于如何在 Windows 桌面上创建暴风雪?的主要内容,如果未能解决你的问题,请参考以下文章
如何在VB.Net中使窗体出现在Windows 10的所有桌面中?