Introduction
This application downloads an image from the internet and puts it as the desktop wallpaper.
There are several sites that show a daily image. One of those is bing.com. It offers great pictures every day as the background in their site. Our application will download images from Bing and two other image providers.
Background
There are a lot of articles about setting up the desktop background, but I'll give this article an internet approach and get cool images from the cloud.
About the code
Basic flow:
The application has eight classes that are distributed in the following way:
DailyImage.cs
This is an abstract class that contains some common methods and defines an abstract method called ImageUrl()
. This method must be overridden by all its "child classes".
using System;
using System.IO;
using System.Net;
using System.Globalization;
namespace Wallpaper.Providers
{
abstract class DailyImage
{
public string DownloadImg(string url) {
string path = Directory.GetCurrentDirectory() + "\\" +
DateTime.Now.ToString("yyyyMMddHHmmssffff",
CultureInfo.InvariantCulture) + ".jpg";
using(WebClient webClient = new WebClient()){
webClient.DownloadFile(url, path);
}
return path;
}
public string Request(string url)
{
string response = "";
using (WebClient webClient = new WebClient())
{
response = webClient.DownloadString(url);
}
return response;
}
public abstract string ImageUrl();
}
}
Bing.cs => Gets the daily image from Microsoft's search service.
using System.Text.RegularExpressions;
namespace Wallpaper.Providers
{
class Bing : DailyImage
{
const string BING_URL = "http://www.bing.com";
public override string ImageUrl(){
string response = base.Request(BING_URL);
string pattern = @"g_img={url:'([a-zA-Z0-9\/\?=%_.]+)";
MatchCollection matches = Regex.Matches(response, pattern);
return (matches.Count > 0) ? matches[0].Groups[1].Value : string.Empty;
}
}
}
Nasa.cs => Gets the daily image from the site: http://apod.nasa.gov/apod/.
using System.Text.RegularExpressions;
namespace Wallpaper.Providers
{
class Nasa : DailyImage
{
const string ASTRONOMY_PICTURE_OF_THE_DAY = "http://apod.nasa.gov/apod/";
public override string ImageUrl(){
string response = base.Request(ASTRONOMY_PICTURE_OF_THE_DAY);
string pattern = @"<IMG SRC=.([a-zA-Z0-9\/\?=%_.]+)";
MatchCollection matches = Regex.Matches(response, pattern);
return (matches.Count > 0) ? matches[0].Groups[1].Value : string.Empty;
}
}
}
Wallppr.cs => Gets a random image from the site: http://www.dailywallppr.com.
using System.Text.RegularExpressions;
namespace Wallpaper.Providers
{
class Wallppr : DailyImage
{
const string WALLPPR_URL = "http://www.dailywallppr.com/index.php?p=pics&id=random";
public override string ImageUrl(){
string response = base.Request(WALLPPR_URL);
string pattern = @"(http://www.dailywallppr.com/prev.*.jpg)";
MatchCollection matches = Regex.Matches(response, pattern);
return (matches.Count > 0) ? matches[0].Groups[1].Value : string.Empty;
}
}
}
NotFound.cs => This class will be instantiated if ProviderFactory is not able to find a valid image provider.
using System;
namespace Wallpaper.Providers
{
class NotFound : DailyImage
{
const string ERROR = "Provider was not found, please check the app.config file";
public override string ImageUrl()
{
Console.WriteLine(ERROR);
Environment.Exit(0);
return string.Empty;
}
}
}
Class Diagram:
ProviderFactory.cs
This class has only one static method called GetInstance
that will return an object according to the specified provider name. I implemented this with reflection instead of using a switch or if/else statements for maintainability purposes.
using System;
using System.Linq;
using System.Reflection;
namespace Wallpaper.Providers
{
class ProviderFactory
{
private ProviderFactory()
{
}
public static DailyImage GetInstance(string name)
{
var found = Assembly.GetExecutingAssembly().GetTypes().SingleOrDefault(t => t.Name == name);
return (found == null) ? new NotFound() : (DailyImage)Activator.CreateInstance(found);
}
}
}
Wallpaper.cs
The class that will be in charge of setting up the desktop background image.
Here's the code:
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Globalization;
namespace Wallpaper
{
class Wallpaper
{
private Wallpaper(){
}
const int SPI_SETDESKWALLPAPER = 20;
const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDWININICHANGE = 0x02;
public enum Style{Centered, Tiled, Stretched}
public static void Set(string path, Style style, bool deleteImg)
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true);
string styleCode = ((int)style).ToString(CultureInfo.InvariantCulture);
key.SetValue("WallpaperStyle", styleCode);
key.SetValue("TileWallpaper", styleCode);
NativeMethods.SystemParametersInfo(SPI_SETDESKWALLPAPER, 0,
path,SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
if (deleteImg)
System.IO.File.Delete(path);
}
}
internal static class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
}
}
Program.cs
This is the file that we all know because by default this file contains the main method, and in our case this file will make the required calls to change the current wallpaper to a new one.
using System;
using Wallpaper.Providers;
using System.Configuration;
namespace Wallpaper
{
class Program
{
static void Main()
{
string provider = ConfigurationManager.AppSettings["provider"];
string deleteImg = ConfigurationManager.AppSettings["delete_img"];
DailyImage oDailyImage = ProviderFactory.GetInstance(provider);
string imageUrl = oDailyImage.ImageUrl();
string localPath = oDailyImage.DownloadImg(imageUrl);
bool deleteFile = (string.IsNullOrEmpty(deleteImg)||(deleteImg.Equals("1")));
Wallpaper.Set(localPath, Wallpaper.Style.Stretched, deleteFile);
}
}
}
Configuration file:
Inside the configuration file of our application you will find:
="1.0"
<configuration>
<appSettings>
<add key="provider" value="Wallppr"/>
<add key="delete_img" value="1"/>
</appSettings>
<startup><supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.0"/></startup></configuration>
<configuration>
</configuration>
Points of Interest
Factory method with reflection:
This technique allows us to keep maintainability in our project and it will help us keep the Open/Closed principle.
Regex:
Very useful when we need to extract data inside other data, in this case the image URLs.
NullObject pattern:
I created a NotFound
class in order to prevent the application from fails when the factory method is unable to find a valid object.
CTO and Co-founder of Golabs, a software development company located in Costa Rica. I am very passionate about my job and I really enjoy building things with software and make things happening. I am also a professor at Universidad Técnica Nacional because I believe that knowledge should be shared, I really enjoy seeing people grow from students at the university to great professionals and entrepreneurs.