This is a Windows Desktop application, that when run, rolls a tumbleweed graphic across the screen. There are several features coded into the application, press "H" while running for help details. This is not my usual posting, but every time it rolls across my desktop, it makes me laugh. This came about after a series of meetings where I was sharing my desktop with my co-workers, and when the opportunity to volunteer for some extra work came up, the meeting was silent. The image in my mind was the one from the movies of a tumbleweed awkwardly rolling across the screen. This little app realizes that image.
For a few more details on "Tumbleweed Moments", you can check out the definition on Urban Dictionary.
Using the code
To activate, just run Tumbleweed.exe.
Points of Interest
While I am not really posting this application for its coding merit, there are some interesting pieces of code:
- The images in and sound that make up what rolls across the screen are any arbitrary images in the "Images" and "Sounds" folders in the application folder. To keep the post small, I have removed all but one of each, but you can play around with different combinations for different effects.
CommandLineDictionary code, used to parse command line parameters is somewhat handy, and is likely code that I will use elsewhere.
HandleKey() method deals with some common key events, like repeating, that I will likely use in other applications.
- The notion of saving settings as just a series of command line arguments (
SaveRunningConfig), while very 1970, still works really well to solve simple problems like this.
- The play sound, with repeat, code is somewhat handy (
PlaySound), and I will likely use it elsewhere.
- The use of multiple
DispatcherTimers to achieve overlaying effects (Fade In, Move Right, Bump Up, etc..) was an interesting exercise. I am sure animation gurus will know better ways to solve problems like this, but for this simple little app, it worked okay.
- The debugging window that shows runtime animation stats is someone interesting. It was more there to help me fine tune the default animation values, but I can see a use case for a similar code on other projects.
I wasn't planning on explaining this code, simply because it is not remarkable. However, it seems there are those that have some interest, so here it goes…
The basic application is a standard WPF application, with the primary window (MainWindow.xaml) being transparent and containing just one control, the image control used to render the tumbleweed. It roughly follows the MVVM pattern, wiring up a view model (VM) (AnimationViewModel.cs). The VM controls all activities and is triggered on load of
MainWindow with a subsequent call to the VM's
The VM looks for sounds in the /Sounds/ sub folder and starts the sound via the
PlaySound() method. Likewise, the VM looks for pictures in the /Pictures/ sub folder and sets the image binding property (
TheImage) via the
All of the animations are predicated on the property binding features of WPF controls. For example, we move the image across the screen by adjusting the bound property
Window.Left and raising the
INotifyPropertyChanged event. I am quite certain this is a terrible way to animate things and likely horribly inefficient, but I tried a few times with GDI libraries and give up. Given the primary intent of this app is humor (and the joy of coding), it met my needs, but also explains why I did not cover the code in more detail in the original posting.
The animation is handled via
DispatchTimers, wired up on start of an action like VM
Bump(), with the actual animation being performed in the
.Tick() event handler (EH). In the case of the primary motion of moving right and rotating, this is done in the
AnimationTimer.Tick EH, just setting the
WindowPosition properties to increment the rotation and move the image across the screen.
In the case of parallel animations, like the "
Bump" when the keyboard is hit, it just starts up a new
BumpTimer) whose Tick EH carries out the up and down arc motion similar to the horizontal motion. The actual bump height (all that code with
Random()) was more just trial and error to find an effect that was pleasing and felt natural.
Similar to the other two timers, the
FadeTimer adjusts the opacity of the image container to achieve the proper fade in and fade out effects.
The program ends based on window position being out of bounds (or on Q key EH), which is checked in the primary
AnimationTimer.Tick EH after each move.
- I did try to use GDI to do the actual animation, and was able to get things to render and move, but it would not paint the desktop underneath after the image moved and I was not able to work past that, which is why I chose the view model bound property approach.
string image file paths to an Image control was a bit challenging. The binding property wants an
ImageSource, which in my case was a
BitmapImage. How I addressed it was,
OnPropertyChange of the
string path to image (
ImagePath), call a method (
SetImage) to load the file, convert it to a
BitmapImage, and then use that to set the
ImageSource property, which raises a secondary event to notify the control of the change.
- The code to play the sound was somewhat similar, but easier since the
MediaPlayer class has a nice
.Open() method to open a file. There were two tricks to achieving the right effects, one was to start playing from a random position (in the
MediaOpened event), and the second was to repeat (in the
CommandLineDictionay class is kind of neat, in that it takes a command line in the form of "
/A:XXXX /B:YYY" and transforms it into a
Dictionary<string,string> which makes parsing and working with parameters much easier. It is not perfect in that things like separators
: within quoted
strings will cause problems, but it is a handy little tool.
- The debug window is just another View bound to the VM that is driving the primary application, dumping out its properties. For better or worse, I dynamically add the bound properties to simplify coding. In hind sight, why I didn't just do this with reflection escapes me, but I am sure there was a reason since I am a fan of reflection. Given there is overhead to raising additional on property change events for things like
WindowLeft (where the image is on the screen), there are precompiler wrappers around them to only compile into a Debug build.
- Getting the right mix of randomness versus consistency with the sound and images was challenging. The
GetFilePath() method is used to assist with that, to try and get a random image/sound the first time, then cycle though the remaining found images in a looping sequence. In the end, I found the one image to be better, so I default to that (by setting
IMAGE_INDEX = -1 it will increment to the valid value of
0 on first run making the default image, image
I hope this satisfies those seeking details about the code. Please review the code and comments, or post questions for additional details, and enjoy!
- 08 June 2014 - Initial public draft
- 14 June 2014 - Updated with technical notes at readers request