Simulating snow…
With the winter not bringing us any snow so far, I thought I’d make some snow myself… I had seen some implementations of “snowmaking” here and there in Silverlight 2.0, but found that some implementations were too complex for just a Sunday morning project. So I figured I’d try it myself…
It is a very simple concept: generate programmatically 200 Image objects, populate them and then have one Storyboard, that fires every milliseconds, go through each one of the flakes, and move them a few pixels further down. Of course, if this were a formal project, I would have made each flake their own class etc., just like Mike did in his example, but this experiment needed to be something simple, quick and still look somewhat realistic.
As you can see from the amount of code, which is relatively little, a lot more can be done here. Maybe rotate the individual flakes, take them from randomly different images, so they are not all the same and speed up some of the animation of the flakes in the background. Also, with the proper background image, you could obscure flakes as they pass behind tree branches or something similar.
mainpage.xaml.cs
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Windows.Media.Imaging; namespace Silverlight_SnowFlake_2 { public partial class Page : UserControl { private Random flakeLocation = new Random(DateTime.Now.Millisecond); private Random flakeSpeed = new Random(DateTime.Now.Millisecond); private Random flakeSize = new Random(DateTime.Now.Millisecond); private Storyboard storyboard = new Storyboard(); public Page() { InitializeComponent(); storyboard = new Storyboard(); storyboard.Duration = TimeSpan.FromMilliseconds(1); storyboard.Completed+= new EventHandler(animateFlakes_Completed); for (int i = 0; i < 200; i++) { Image vlok = new Image(); vlok.Source = new BitmapImage(new Uri("sneeuwvlok.png", UriKind.Relative)); vlok.SetValue(Canvas.ZIndexProperty, i+1); ScaleTransform flakeScale = new ScaleTransform(); flakeScale.ScaleX = flakeSize.NextDouble(); flakeScale.ScaleY = flakeScale.ScaleX; TranslateTransform flakeTranslate = new TranslateTransform(); //need to stretch the width by as much as scaled down flakeTranslate.X = (double)flakeLocation.Next(0, (int)(LayoutRoot.Width / flakeScale.ScaleX)); flakeTranslate.Y = (double)flakeLocation.Next(0, (int)(LayoutRoot.Height / flakeScale.ScaleX)); TransformGroup flakeTransform = new TransformGroup(); flakeTransform.Children.Add(flakeTranslate); flakeTransform.Children.Add(flakeScale); vlok.RenderTransform = flakeTransform; LayoutRoot.Children.Add(vlok); } this.storyboard.Begin(); } private void animateFlakes_Completed(object sender, EventArgs e) { //go through the entire collection of flakes foreach (UIElement element in LayoutRoot.Children) { if (element.GetType() == typeof(Image) && (element as Image).Name != "backgroundImage") { //create an object to be accessed below TransformCollection flakeTransforms = (TransformCollection)element.RenderTransform.GetValue( TransformGroup.ChildrenProperty); if ( ((TransformGroup)element.RenderTransform).Value.OffsetY> LayoutRoot.Height || ((TransformGroup)element.RenderTransform).Value.OffsetX> LayoutRoot.Width) { //places flake back at the top ((TranslateTransform)flakeTransforms[0]).Y = -20; //distributes flake randomly across the top ((TranslateTransform)flakeTransforms[0]).X = (double)flakeLocation.Next(0, (int)(LayoutRoot.Width / (flakeTransforms[1] as ScaleTransform).ScaleX)); } //adds the flake animation by moving the flake via X and Y ((TranslateTransform)flakeTransforms[0]).Y += 2 * (flakeTransforms[1] as ScaleTransform).ScaleX; //random move only the larger snowflakes if ((flakeTransforms[1] as ScaleTransform).ScaleX > 0.9) { ((TranslateTransform)flakeTransforms[0]).X += (double)this.flakeSpeed.NextDouble(); } } } //start the storyboard again this.storyboard.Begin(); } } }
mainpage.xaml
<UserControl x:Class="Silverlight_SnowFlake_2.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300"> <Canvas x:Name="LayoutRoot" Width="374" Height="507"> <Image x:Name="backgroundImage" Source="backgroundImage.jpg" Canvas.ZIndex="0" /> <Rectangle Stroke="Black" StrokeThickness="10" Width="388" Height="521" Canvas.ZIndex="1001"/> </Canvas> </UserControl>