En aras de la brevedad y la simplicidad, esta respuesta se ha reducido a un conjunto limitado de requisitos. Es trivial expandir esto y hacerlo más robusto.
Esta respuesta usa WPF en Windows XP. Debería funcionar en hasta 2 monitores, y debería funcionar en otros sistemas de Windows también.
Se inicia con una simple ventana:
<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>
Para esta ventana, vamos a añadir los copos de nieve definidos de la siguiente manera:
<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>
Los copos de nieve tienen el control de usuario por defecto de código subyacente sin cambios.
Por último, la ventana 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;
}
}
}
}
}
tuve que usar un poco de código formas para conseguir las cosas multipantalla, y he tenido que incluir las referencias a las Asambleas en mi proyecto.
No lo he probado mucho, pero funciona en mi sistema, y la nieve se queda en la parte inferior de la pantalla cuando termina.
He usado this referencia para el comportamiento de clic.
Un compañero más dedicado de lo que debería ser capaz de adaptar this, además de un poco de detección de bordes a la tarea de hacer que la nieve se siente en otro lugar.
Tenga en cuenta que los copos de nieve nunca se limpian en este ejemplo, y después de ejecutarlo el tiempo suficiente, puede notar cierta desaceleración.
Have Fun!
Puedo satisfacer todas las 5 características requeridas al dejar mi computadora en los escalones frente a mi casa en Nueva York esta tarde. –
@Larry Lustig - punto tomado, sin embargo, esperaba una solución de software más, con la publicación en SO y todo ... –
Para el detractor (votante cercano) - ¿qué no entiendes? ¿Debo aclarar más? –