Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Wallpaper Cycler

  This sample demonstrate simple wallpaper cycler. It expects a folder in which it will find images. Upon startup, it will change the wallpaper, and it will even modify the placement behavior based on the size of the image found.
Arian Kulp's Blog

Difficulty: Easy
Time Required: 1-3 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware:
Download:

A number of articles ago, a simple wallpaper utility was written. I thought it would be nice to put together some of the concepts that we've looked at recently and create a new, enhanced wallpaper changer. Specifically, this application is no longer based on a specific file. Instead, it expects a folder in which it will find images. Upon startup, it will change the wallpaper, and it will even modify the placement behavior based on the size of the image found.

You can read the article now, but for added benefit download a copy of Visual Studio 2005 Express Edition for C# or Visual Basic. These are free if downloaded during most of 2006, and provide all of the tools necessary to create great applications. After you install Visual Studio Express, download the code for this article from the links at the top. Choose from C# or Visual Basic, according to your personal preference—the applications are functionally identical.

As with most recent In the Box columns, this application will start with no visible interface beyond its notification icon in the system tray. The available options will be to display the settings, change the wallpaper on-demand, or exit. The "Show Settings" option is in bold. This is a visual cue that if you double-click the icon, this is the action that will take place. There is no built-in support for this feature. Simply set the menu item's Font.Bold property to "True" and create a MouseDoubleClick event handler for the NotifyIcon control.

Figure 1: The notification icon menu

Scanning folders

The heart of the application is its ability to scan a folder randomly to find an image to display as the desktop background. This is surprisingly easy using the System.IO.Directory class. Its static/shared GetFiles method returns a collection of all files in a given folder. You can optionally pass a wildcard pattern, and a recursive qualifier. It would have made sense to specify a wildcard pattern including extensions like .JPG and .GIF, but apparently only a single expression can be used. The third parameter takes an argument of type System.IO.SearchOption. This is where you can specify a top-level file listing (TopDirectoryOnly), or a recursive (AllDirectories) one.

Visual Basic

' Specify top-level or sub-folders
Dim opt As System.IO.SearchOption = _
     System.IO.SearchOption.TopDirectoryOnly

If subfoldersCheckbox.Checked Then
    opt = System.IO.SearchOption.AllDirectories

End If

' Grab complete list of files
Dim files As String() = System.IO.Directory.GetFiles( _
    picturesPathTextBox.Text, "*", opt)

Visual C#

// Specify top-level or sub-folders
System.IO.SearchOption opt = System.IO.SearchOption.TopDirectoryOnly;
if( FsubfoldersCheckbox.Checked ) opt = 
    System.IO.SearchOption.AllDirectories;

// Grab complete list of files
string[] files = System.IO.Directory.GetFiles(
picturesPathTextBox.Text, "*", opt);

At this point, the files object contains an array of strings. Each entry is a fully-qualified filename from within the supplied folder. It would be nice to just randomly select an entry from this collection, but recall that we were not able to filter based on file extension. This must be done now. Two other exclusions also apply. First, we don't want to select the file that was used last time (we save one time back). Second, we don't want to select the temporary image that we generate (discussed later). The following code demonstrates copying the array into a generic List collection, element by element, provided that each element meets our exacting criteria.

Visual Basic

' Filter list to remove non-images, last image shown,
' and the app-generated BMP file from a previous run.
Dim filteredFiles As New List(Of String)()

For Each file As String In files

    Dim ext As String = file.Substring(file.LastIndexOf("."))

    If ".jpg .bmp .gif".IndexOf(ext) > -1 AndAlso _
        file.EndsWith("coding4fun-desktop.bmp") = False AndAlso _
        Not file = My.Settings.LastImageShown Then

        filteredFiles.Add(file)
    End If
Next

Visual C#

// Filter list to remove non-images, last image shown,
// and the app-generated BMP file from a previous run.

List<String> filteredFiles = new List<string>();
foreach (string file in files)
{
    string ext = file.Substring(file.LastIndexOf("."));

    if (".jpg .bmp .gif".IndexOf(ext) > -1 &&

        !file.EndsWith("coding4fun-desktop.bmp") &&
        file != settings.LastImageShown)
    {
        filteredFiles.Add(file);
    }
}

After all of this processing, it is possible that no files remain in our collection. Perhaps the folder was empty initially, none of the files were images, or the only image found was used last time. Either nothing will be returned, or a random index will be generated to grab one element out of the collection. Recall that these elements are full pathnames. We will save that pathname as the LastImageShown setting, create a Bitmap object given the pathname, then return that object.

Visual Basic

' Make sure there are any files left
If filteredFiles.Count = 0 Then Return Nothing

' Randomly grab a file
Dim filename As String = filteredFiles(rnd.Next(filteredFiles.Count))


' Remember last image shown
My.Settings.LastImageShown = filename
My.Settings.Save()

' Return the bitmap object from this filename
Return New Bitmap(filename)

Visual C#

// Make sure there are any files left
if (filteredFiles.Count == 0) 
    return null;


// Randomly grab a file
string filename = filteredFiles[rnd.Next(filteredFiles.Count)];

// Remember last image shown
settings.LastImageShown = filename;
settings.Save();

// Return the bitmap object from this filename
return new Bitmap(filename);

Finally the image is saved as a BMP file in the My Pictures folder, and passed to the appropriate system API function, SystemParametersInfo, to effect the change. Most of this is the same as in the original In the Box column referenced above, but the original required an image to be in BMP format to begin with. As it turns out, once you have loaded an image into a Bitmap object, saving as a BMP file format is a single line of code:

Visual Basic

' Convert to BMP and save

img.Save(picturesPath, System.Drawing.Imaging.ImageFormat.Bmp)

Visual C#

// Convert to BMP and save)
img.Save(picturesPath, System.Drawing.Imaging.ImageFormat.Bmp);

Study the SetDesktopBackground static/shared method in WindowsAPI.vb/.cs to see the implementation details.

Settings

Because the image to display can vary wildly each time, the settings dialog includes a number of behavioral settings. The first checkbox allows you to specify if sub-folders should be scanned or not. If you are pointing at a collection of home photos divided into sub-folders for albums, this could be useful.

The three ComboBox controls allow some flexibility in how images are displayed depending on their size relative to the desktop size. By default, images larger than the desktop will be stretched to fit. Now the term "stretched" is taken from the Windows Display control panel. It's a bit of a misnomer, since what it really means is "stretched or shrunk" in either direction to exactly fill the screen. It's really ugly if you have a widescreen display and conventional (4:3) images.

Images smaller than the screen will be centered. The final category, Tiled, is a bit more flexible. If you have very small images, you may want to tile them for a repeating pattern. Just specify how small is small (128 pixels in both directions, by default), specify the action (it doesn't need to be Tiled), and watch it go. I personally like the defaults, but it made some sense to allow them to be preferences so anyone can change them easily.

Figure 2: Settings dialog

All of the settings directly correspond to project settings defined in Visual Studio. This nifty feature lets you define settings at the user or application level, set a datatype, and even a default value. Then, to complete the coolness, you can actually databind these settings to the controls. All you need to do is load and save when necessary. The mechanics of copying data between controls and settings are taken care of for you!

Figure 3: Databinding settings

Working with the Registry

The application must write to the registry in order to set the stretched/tiled/centered attributes. This is very easy using the Registry object. Just use a static property, such as CurrentUser to access the HKEY_CURRENT_USER key. You can dig deeper by using a method like OpenSubKey to open nested keys and access their values. The wallpaper cycler application also uses the registry to add itself to Windows startup. The auto-start CheckBox control calls some shared/static methods in the WindowsAPI class to add and remove itself:

Visual Basic

' Based on checkbox, call API functions to add or remove
' application path from registry Run section for current user.

If autostartCheckbox.Checked Then
    WindowsAPI.AddStartupItem("BackgroundCycler", _ 
    System.Reflection.Assembly.GetEntryAssembly().Location)
Else
    WindowsAPI.RemoveStartupItem("BackgroundCycler")

End If

Visual C#


// Based on checkbox, call API functions to add or remove
// application path from registry Run section for current user.
            if (autostartCheckbox.Checked)
            {
                WindowsAPI.AddStartupItem("BackgroundCycler",
                    System.Reflection.Assembly.GetEntryAssembly().Location);
            }
            else

            {
                WindowsAPI.RemoveStartupItem("BackgroundCycler");
            }

Notice the call to System.Reflection.Assembly class. The GetEntryAssembly method returns a reference to the assembly that contained the startup code for the current application. The Location property returns the assembly filename, in this case the path to the executable file. In the AddStartupItem method, the sub-key for the current user's Run entries is opened, and a new value is added. So simple, but so powerful:

Visual Basic

Dim key As RegistryKey = _
    Registry.CurrentUser.OpenSubKey( _
    "Software\Microsoft\Windows\CurrentVersion\Run", True)
key.SetValue(name, path)

Visual C#

RegistryKey key = Registry.CurrentUser.OpenSubKey(
                @"Software\Microsoft\Windows\CurrentVersion\Run", true);

            key.SetValue(name, path);

Potential Enhancements

The application is ready to use, and fun too! You can change wallpaper frequently, even if your images are not already in BMP format, and you don't need to go into Desktop properties to do it. It might also be fun to add an option to automatically change images on a timed interval. Another challenge would be to provide the ability to download images at random from URLs. You could provide a URL to an image gallery, scan the HTML for image links, then randomly select one to download, convert, and show as wallpaper. Lots of options!

Conclusion

I hope this got you thinking about things to do with the Bitmap object, the Registry object, and desktop interaction. Be sure to visit http://msdn.microsoft.com/vstudio/express/ to download your preferred version of Visual Studio 2005 Express Edition. Then, take a look at the source code for this project, learn, and do even more. Good luck, and best of all, have fun!

Tags:

Follow the Discussion

  • Hi Sam! Photos i send on e-mail. &#10;Green,GreenHi Sam! Photos i send on e-mail. Green,Green

    Hi Sam! Photos i send on e-mail.

    Green,Hi Sam! Photos i send on e-mail.

    Green

  • Matt McCormickMatt McCormick

    I am having trouble with this, I can't seem to get the wallpaper to stay. I can set a wallpaper, and sometimes it will stay the wallapaper until I log out, and sometimes it wont stay at all. By stay, I mean the wallpaper I set appears, then later its just a blue background (or whatever the default background color is). Any help is appreciated... mbmccormick@gmail.com.

  • dshomedshome

    SystemParametersInfo does not work in Windows Vista

  • tristan cornertristan corner

    this is very useful infomation because a made a good progran from it.

    [system error]

  • kirupaBlog - If it isn&#8217;t broken, take it apart and fix it!  » Blog Archive   » Interesting Links #3: Write Better Code, AK-47, Webcams&#8230;kirupaBlog - If it isn’t broken, take it apart and fix it! » Blog Archive » Interesting Links #3: Write Better Code, AK-47, Webcams…

    PingBack from http://blog.kirupa.com/?p=116

  • MSDN Blog Postings  » MultiWall - Wallpaper Tool for Multiple MonitorsMSDN Blog Postings » MultiWall - Wallpaper Tool for Multiple Monitors

    PingBack from http://msdnrss.thecoderblogs.com/2007/10/02/multiwall-wallpaper-tool-for-multiple-monitors-3/

  • Noticias externasNoticias externas

    Are you more productive with more monitors? Would you be even more productive with better wallpaper management

  • Clint RutkasClint I'm a "developer"

    Are you more productive with more monitors? Would you be even more productive with better wallpaper management?

  • JackJack

    Cool! I had the same idea, and I'd like to make it in Python!

    My problem is how to say to Windows XP which image to load... (API). Some suggestion?

    Thanks

  • SlumberMachineSlumber​Machine

    I had some issues with errors on vista and vb 2008. I made the following change and it worked:

           Dim filteredFiles As New List(Of String)()

           For Each file As String In files

               If file.LastIndexOf(".") > 0 Then

                   Dim ext As String = file.Substring(file.LastIndexOf("."))

                   If ".jpg .bmp .gif".IndexOf(ext) > -1 AndAlso _

                      file.EndsWith("coding4fun-desktop.bmp") = False AndAlso _

                     Not file = My.Settings.LastImageShown Then

                       filteredFiles.Add(file)

                   End If

               End If

           Next

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.